在Java中停止循环线程

在Java中停止循环线程,java,multithreading,Java,Multithreading,我正在使用一个从队列中连续读取的线程 比如: public void run() { Object obj; while(true) { synchronized(objectsQueue) { if(objectesQueue.isEmpty()) { try { objectesQueue.wait(); } catch (Inter

我正在使用一个从队列中连续读取的线程

比如:

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
    }
}
停止此线程的最佳方法是什么

我看到两种选择:

1-由于
Thread.stop()
已被弃用,我可以实现一个
stopThistThread()
方法,该方法使用一个n原子检查条件变量

2-将死亡事件对象或类似对象发送到队列。当线程获取死亡事件时,它退出

我更喜欢第1种方法,但是,我不知道什么时候调用
stopThisThread()
方法,因为可能有东西在通往队列的路上,而停止信号可能会首先到达(不可取)


有什么建议吗?

方法1是首选方法


只需将volatile
stop
字段设置为true,并在正在运行的线程上调用
interrupt()
。这将强制等待返回的任何I/O方法使用
InterruptedException
(如果您的库编写正确,这将得到优雅的处理)。

如果您需要在关闭前完成队列上的所有工作,那么
DeathEvent
(或者通常称为“毒丸”)方法可以很好地工作。问题是这可能需要很长时间

如果你想尽快停下来,我建议你这样做

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.
   }
}
BlockingQueue=。。。
...
公开募捐{
试一试{
//以下测试是获得快速中断所必需的。如果
//将其替换为“true”,队列将在
//打断被注意到了。(谢谢蒂姆)
而(!Thread.interrupted()){
O obj=queue.take();
剂量测定法(obj);
}
}捕获(中断异常例外){
//我们完了。
}
}
要停止使用
run
方法实例化的线程
t
,只需调用
t.interrupt()

如果将上面的代码与其他答案进行比较,您会注意到使用
阻塞队列
线程.interrupt()
是如何简化解决方案的


我还想说,额外的
stop
标志是不必要的,而且从总体上看,可能是有害的。行为良好的工作线程应该尊重中断。意外中断仅仅意味着工作进程正在原始程序员没有预料到的上下文中运行。最好的办法是,如果工人按照要求去做。。。i、 它应该停止。。。这是否符合最初程序员的概念。

我认为您的两个案例实际上表现出相同的潜在行为。对于第二种情况,考虑线程A添加了DyTeVoter,线程B添加了一个FoEvEnter。当您的作业线程接收到DeatheEvent时,它后面仍然有一个FooEvent,这与您在选项1中描述的场景相同,除非您尝试在返回之前清除队列,但实际上您是在让线程保持活动状态,而您要做的是停止它

我同意你的看法,第一种选择更可取。潜在的解决方案取决于队列的填充方式。如果它是工作线程类的一部分,则可以让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;
  }
}

然后,试图将事件排入队列以适当处理它的对象将负责,但至少它会知道事件不在队列中,并且不会被处理。

我通常在包含线程的类和线程代码中放置一个标志。(注意:不是while(true),而是while(flag))

然后在类中创建一个方法,将标志设置为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
        }
    }

在读卡器线程中,有一个布尔变量stop。当您希望此线程停止时,将thius设置为true并中断线程。在读卡器线程中,当安全时(当您没有未处理的对象时),检查stop变量的状态,如果设置了,则返回循环外。如下所示

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.
     }
}

为什么不使用一个调度器,您可以在需要时简单地停止它呢?标准调度程序支持重复调度,也就是在重新调度新运行之前等待工作线程完成

ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
service.scheduleWithFixedDelay(myThread, 1, 10, TimeUnit.SECONDS);
此示例将以10秒的延迟运行线程,这意味着当一次运行完成时,它将在10秒后重新启动。而不是重新发明你得到的轮子

service.shutdown()
while(true)不再是必需的


注意,您的
等待
应该在
while
循环中,而不是
if
,以防止虚假唤醒。对于所有依赖中断的人:只想说,生活从来没有这么简单,有时人们会对不可中断的“活动”调用中断而这个活动绝对不会注意到中断。这是经验使然。尽管如此,我不知道还有更好的方法。。。请仔细查看API:查看中断()method@halfwarp史蒂芬C的回答是+1和+1。你的“死亡事件”实际上被称为“毒药丸”:你只需将一个特殊的对象(毒药丸)排队,这意味着一旦你将其排队,它就意味着“停止线程”。这样,“可能在进入队列的路上”的任何东西[sic]在毒丸之前都会被处理。@Yaneeve:这就是为什么你必须在所有应该处理中断的代码中调用
isInterrupted()
。但这并不意味着它是错误的和/或不可能使用。事实上,这是在I/O调用中告诉阻塞线程检查某些条件的唯一方法。@Joachim:正如我所说的,根据经验,有时候你没有这种特权。在我的例子中,如果被中断的代码由第三方库运行,而该库不“感觉”检查被中断的状态,那么事情就会变得有点复杂。为了不那么含糊,我遇到了一个问题,即取消发送的一个尚未超时的CORBA请求…看起来不太一样