简体   繁体   中英

Thread is pausing but not resuming

I am fairly new to threads and I have a problem with them. (I'm implementing the runnable interface) I first call the start method and it starts the thread. After a while I call the pause method, works aswell. When I resume it though, I get the message that the Thread is exiting.

I think it is because of the variable "suspended"

Here is my code:

 public class Race implements Runnable {
    public Thread t = null;
    private String threadName = "Race";
    private int sleepTime = 1000; // ms Wartezeit. Wird in run eingesetzt
    private boolean suspended = false;

    public Race() {
       //Start other classes
       //Resume, Start and Pause get called from other class's ActionListener
    }

    public static void main(String[] args) {
       Race r = new Race();
    }

    @Override
    public void run() {

        int i = 0;
        //lebendig = true;
        try {
            while (!suspended) {
                //Code goes here

                Thread.sleep(sleepTime);
            }
        } catch (InterruptedException e) {
            System.out.println("Thread " + threadName + " interrupted.");
        }

        System.out.println("Thread " + threadName + " is exiting");
    }

    public void start() {
        suspended = false;
        System.out.println("Starting " + threadName);
        if (t == null) {
            t = new Thread(this, threadName);
            t.start();
        }
    }

    public void pause() {
        suspended = true;
        t = null;
    }

    synchronized void resume() {
        suspended = false;
        notify();
    }
    public void pause() {
        suspended = true;
        t = null;
    } 
}

Here's a sample Runnable from one of my projects. This Runnable maintains a timer that can be started and stopped.

package com.ggl.kakurasu.runnable;

import javax.swing.SwingUtilities;

import com.ggl.kakurasu.model.KakurasuModel;
import com.ggl.kakurasu.view.KakurasuFrame;

public class ElapsedTimeRunnable implements Runnable {

    private volatile boolean running;
    private volatile boolean solved;

    private KakurasuFrame frame;

    private KakurasuModel model;

    public ElapsedTimeRunnable(KakurasuFrame frame, KakurasuModel model) {
        this.frame = frame;
        this.model = model;
        this.running = true;
        this.solved = false;
    }

    @Override
    public void run() {
        while (running) {
            long sleepTime = solved ? 500L : 5L;
            while (!solved) {
                String elapsedTimeString = model.getElapsedTime(System
                        .currentTimeMillis());
                updateControlPanel(elapsedTimeString);
                sleep(50L);
            }
            sleep(sleepTime);
        }
    }

    private void updateControlPanel(final String elapsedTimeString) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                frame.updateControlPanel(elapsedTimeString);
            }
        });
    }

    private void sleep(long sleepTime) {
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {

        }
    }

    public synchronized void setRunning(boolean running) {
        this.running = running;
    }

    public synchronized void setSolved(boolean solved) {
        this.solved = solved;
    }

}

The boolean fields are marked volatile so that the values can be changed from any thread. The methods that set the boolean fields are marked synchronized so that only one thread at a time can update the boolean fields.

As you can see, there are 2 booleans that are used to control the run method. The running boolean is set to false when we want to stop the Runnable completely. We start the thread containing the Runnable once . We stop the thread containing the Runnable once .

The solved boolean starts and stops the timer loop. When the solved boolean is false, the timer display is updated every 50 milliseconds, which gives an accurate display to the tenth of a second.

When the solved boolean is true, the timer stops. The outer loop still runs. We sleep for 500 milliseconds so that we're not taking a whole lot of CPU time, but the code can respond when the timer is started again.

Points to remember: A thread can only be started once. A thread can only be stopped once. If you want to pause a thread, you have to provide alternate code in the run method so the thread continues to run.

I thinking having a Runnable and which starts a Thread which runs said Runnable may be adding to the confusion, especially since the methods you are giving Race are similar to those of Thread (namely start() , suspend() and resume() though the latter two are deprecated ). But fundamentally the issues are:

  1. There is nothing special about notify() concerning Runnables, and calling notify only resolves blocked code initiated by wait() .
  2. The while loop in run() terminates whenever you set suspend as true, rather blocking with wait() .

The following demo shows how to write this a Thread (avoiding the creation of extra handles, rewriting start() , etc.) and I've used unpause() because resume() is a final method in Thread .

import java.io.PrintStream;

public class Race extends Thread {
   private PrintStream logStream = null;
   private boolean paused = false;
   private int sleepTime = 100;

   public static void main(String[] args){
      try{
         Race r = new Race("Race Thread", System.out);
         r.start();  //provided by Thread
         Thread.sleep(1000);
         r.pause();  //test
         Thread.sleep(1000);
         synchronized(r){r.notify();} //improper
         Thread.sleep(1000);
         r.unpause(); //proper
      }
      catch(Exception e){}
   }

   public Race(String name, PrintStream log){
      super(name);
      logStream = log;
   }

   public void run(){
      //runs only on assigned thread
      if(Thread.currentThread() != this)
         return;
      log("starting");
      //define parameters
      int i=0;
      //run until break
      while(true){
         //actions
         System.out.println(i++);
         //exit(optional)
         if(i==20)
            break;
         try{ Thread.sleep(sleepTime); }
         catch(InterruptedException e){}
         //WAIT LOOP ILLUSTRATION
         if(paused){
            log("pausing");
            while(true){
               try{ synchronized(this){ wait();} }
               catch(IllegalMonitorStateException e){log(e.toString());}
               catch(InterruptedException e){log(e.toString());}
               if(!paused){
                  log("resuming");
                  break;
               }
               log("falsely notified");
            }
         }
      }
      log("exiting");
   }

   public void pause(){
      if(!paused)
         paused = true;
   }

   public void unpause(){
      if(paused){
         paused = false;
         synchronized(this){ notify(); }
      }
   }

   private void log(String s){
      if(logStream!=null)
         logStream.println(getName()+" is "+s);
   }
}

Note that to providing pausing functionality the wait() command has to be in loop, otherwise any notify() may unblock it. Also that wait() and notify() are called with synchronization.

Finally my Wait Loop Illustration is more complicated than it needs to be (so that it would illustrate a potentially improper call to notify by another thread). You can probably get by with:

while(paused){
   try{ synchronized(this){ wait();} }
   catch(IllegalMonitorStateException e){}
   catch(InterruptedException e){}
}

If you wanted to continue building everything as a Runnable , perhaps to make a single process be carried out by multiple threads (sequentially) then you would need other small change. For instance, run() should not initialize your variables because every Thread you start will begin run() and reset data (or worse, not have access to instance variables declared in run() ). However, you would still use wait and notify in the same way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM