Stopping looping thread in Java

后端 未结 6 1617
我寻月下人不归
我寻月下人不归 2020-12-29 16:13

I\'m using a thread that is continuously reading from a queue.

Something like:

public void run() {
    Object obj;
    while(true) {
        synchron         


        
相关标签:
6条回答
  • 2020-12-29 16:14

    In your reader thread have a boolean variable stop. When you wish for this thread to stop set thius to true and interrupt the thread. Within the reader thread when safe (when you don't have an unprocessed object) check the status of the stop variable and return out of the loop if set. as per below.

    public class readerThread extends Thread{
        private volitile boolean stop = false;
        public void stopSoon(){
            stop = true;
            this.interrupt();
        }
        public void run() {
            Object obj;
            while(true) {
                if(stop){
                    return;
                }
                synchronized(objectsQueue) {
                if(objectesQueue.isEmpty()) {
                    try {
                        objectesQueue.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if(stop){
                        return;
                    }    
                    obj = objectesQueue.poll();
                    // Do something with the Object obj
                }
            }
        }
    
    
    }
    public class OtherClass{
         ThreadReader reader;
         private void start(){
              reader = ...;
              reader.start();
         }
    
         private void stop(){
              reader.stopSoon();
              reader.join();     // Wait for thread to stop if nessasery.
         }
    }
    
    0 讨论(0)
  • 2020-12-29 16:18

    I usually put a flag in the class that has the Thread in it and in my Thread code I would do. (NOTE: Instead of while(true) I do while(flag))

    Then create a method in the class to set the flag to false;

    private volatile bool flag = true;
    
    public void stopThread()
    {
       flag = false;
    }
    
        public void run() {
            Object obj;
            while(flag) {
                synchronized(objectsQueue) {
                    if(objectesQueue.isEmpty()) {
                        try {
                            objectesQueue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        obj = objectesQueue.poll();
                    }
                }
    
                // Do something with the Object obj
            }
        }
    
    0 讨论(0)
  • 2020-12-29 16:19

    I think your two cases actually exhibit the same potential behavior. For the second case consider Thread A adds the DeathEvent after which Thread B adds a FooEvent. When your job Thread receives the DeathEvent there is still a FooEvent behind it, which is the same scenario you are describing in Option 1, unless you try to clear the queue before returning, but then you are essentially keeping the thread alive, when what you are trying to do is stop it.

    I agree with you that the first option is more desirable. A potential solution would depend on how your queue is populated. If it is a part of your work thread class you could have your stopThisThread() method set a flag that would return an appropriate value (or throw Exception) from the enqueuing call i.e.:

    MyThread extends Thread{
      boolean running = true;
    
      public void run(){
        while(running){
          try{
            //process queue...
          }catch(InterruptedExcpetion e){
            ...
          }
        }
      }
    
      public void stopThisThread(){
        running = false;
        interrupt();
      }
    
      public boolean enqueue(Object o){
        if(!running){
           return false;
             OR
           throw new ThreadNotRunningException();
        }
        queue.add(o);
        return true;
      }
    }
    

    It would then be the responsibility of the object attempting to enqueue the Event to deal with it appropriately, but at the least it will know that the event is not in the queue, and will not be processed.

    0 讨论(0)
  • 2020-12-29 16:20

    The DeathEvent (or as it is often call, "poison pill") approach works well if you need to complete all of the work on the queue before shutting down. The problem is that this could take a long time.

    If you want to stop as soon as possible, I suggest you do this

    BlockingQueue<O> queue = ...
    
    ...
    
    public void run() {
       try {
           // The following test is necessary to get fast interrupts.  If
           // it is replaced with 'true', the queue will be drained before
           // the interrupt is noticed.  (Thanks Tim)
           while (!Thread.interrupted()) {
               O obj = queue.take();
               doSomething(obj);
           }
       } catch (InterruptedException ex) {
           // We are done.
       }
    }
    

    To stop the thread t that instantiated with that run method, simply call t.interrupt();.

    If you compare the code above with other answers, you will notice how using a BlockingQueue and Thread.interrupt() simplifies the solution.

    I would also claim that an extra stop flag is unnecessary, and in the big picture, potentially harmful. A well-behaved worker thread should respect an interrupt. An unexpected interrupt simply means that the worker is being run in a context that the original programmer did not anticipate. The best thing is if the worker to does what it is told to do ... i.e. it should stop ... whether or not this fits with the original programmer's conception.

    0 讨论(0)
  • 2020-12-29 16:23

    Why not use a scheduler which you simply can stop when required? The standard scheduler supports repeated scheduling which also waits for the worker thread to finish before rescheduling a new run.

    ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
    service.scheduleWithFixedDelay(myThread, 1, 10, TimeUnit.SECONDS);
    

    this sample would run your thread with a delay of 10 sec, that means when one run finishes, it restarts it 10 seconds later. And instead of having to reinvent the wheel you get

    service.shutdown()
    

    the while(true) is not necessary anymore.

    ScheduledExecutorService Javadoc

    0 讨论(0)
  • 2020-12-29 16:40

    Approach 1 is the preferred one.

    Simply set a volatile stop field to true and call interrupt() on the running thread. This will force any I/O methods that wait to return with an InterruptedException (and if your library is written correctly this will be handled gracefully).

    0 讨论(0)
提交回复
热议问题