简体   繁体   English

在Java中停止循环线程

[英]Stopping looping thread in Java

I'm using a thread that is continuously reading from a queue. 我正在使用一个不断从队列中读取的线程。

Something like: 就像是:

public void run() {
    Object obj;
    while(true) {
        synchronized(objectsQueue) {
            if(objectesQueue.isEmpty()) {
                try {
                    objectesQueue.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                obj = objectesQueue.poll();
            }
        }

        // Do something with the Object obj
    }
}

What is the best way to stop this thread? 停止此线程的最佳方法是什么?

I see two options: 我看到两个选择:

1 - Since Thread.stop() is deprecated, I can implement a stopThisThread() method that uses an atomic check-condition variable. 1 - 由于不推荐使用Thread.stop() ,我可以实现一个使用原子检查条件变量的stopThisThread()方法。

2 - Send a Death Event object or something like that to the queue. 2 - 将死亡事件对象或类似内容发送到队列。 When the thread fetches a death event, it exits. 当线程获取死亡事件时,它将退出。

I prefer the 1st way, however, I don't know when to call the stopThisThread() method, as something might be on it's way to the queue and the stop signal can arrive first (not desirable). 我更喜欢第一种方式,但是,我不知道何时调用stopThisThread()方法,因为它可能在队列的路上,并且停止信号可以首先到达(不可取)。

Any suggestions? 有什么建议么?

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. 如果您需要在关闭之前完成队列中的所有工作, DeathEvent (或者通常称为“毒丸”)方法很有效。 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(); 要停止使用该run方法实例化的线程t ,只需调用t.interrupt(); .

If you compare the code above with other answers, you will notice how using a BlockingQueue and Thread.interrupt() simplifies the solution. 如果将上面的代码与其他答案进行比较,您会注意到如何使用BlockingQueueThread.interrupt()简化解决方案。

I would also claim that an extra stop flag is unnecessary, and in the big picture, potentially harmful. 我还声称不需要额外的stop标志,从大局来看,可能有害。 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 ... ie it should stop ... whether or not this fits with the original programmer's conception. 最好的事情是如果工人做了被告知要做的事情......即它应该停止......这是否符合原始程序员的概念。

In your reader thread have a boolean variable stop. 在你的读者线程中有一个布尔变量stop。 When you wish for this thread to stop set thius to true and interrupt the thread. 当你希望这个线程停止将thius设置为true并中断线程时。 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.
     }
}

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. 此示例将以10秒的延迟运行您的线程,这意味着当一次运行完成时,它会在10秒后重新启动它。 And instead of having to reinvent the wheel you get 而不是必须重新发明轮子你得到

service.shutdown()

the while(true) is not necessary anymore. while(true)不再是必要的了。

ScheduledExecutorService Javadoc ScheduledExecutorService Javadoc

Approach 1 is the preferred one. 方法1是优选的方法。

Simply set a volatile stop field to true and call interrupt() on the running thread. 只需将volatile stop字段设置为true,并在正在运行的线程上调用interrupt() 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). 这将强制任何等待以InterruptedException返回的I / O方法(如果您的库被正确写入,则将正常处理)。

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. 对于第二种情况,考虑线程A添加DeathEvent,之后线程B添加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. 当你的作业Thread接收到DeathEvent时,它背后仍然有一个FooEvent,这与你在选项1中描述的场景相同,除非你在返回之前尝试清除队列,但是你基本上保持线程活着,当你试图做的就是阻止它。

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 ie: 如果它是你的工作线程类的一部分,你可以让你的stopThisThread()方法设置一个标志,该标志将从排队调用中返回一个适当的值(或抛出异常),即:

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. 然后,尝试将事件排入队列以适当地处理它的对象将负责,但至少它将知道事件不在队列中,并且不会被处理。

I usually put a flag in the class that has the Thread in it and in my Thread code I would do. 我通常在包含Thread的类中放置一个标志,在我的Thread代码中我会这样做。 (NOTE: Instead of while(true) I do while(flag)) (注意:而不是while(true)我做的同时(旗帜))

Then create a method in the class to set the flag to false; 然后在类中创建一个方法,将标志设置为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
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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