在Java中停止循环线程

时间:2010-04-27 11:25:27

标签: java multithreading

我正在使用一个不断从队列中读取的线程。

类似的东西:

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(),我可以实现一个使用n原子检查条件变量的stopThisThread()方法。

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

我更喜欢第一种方式,但是,我不知道何时调用stopThisThread()方法,因为它可能会出现在队列中,并且停止信号可以首先到达(不可取)。 / p>

有什么建议吗?

6 个答案:

答案 0 :(得分:6)

如果您需要在关闭之前完成队列中的所有工作,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.
   }
}

要停止使用该t方法实例化的主题run,只需致电t.interrupt();

如果您将上述代码与其他答案进行比较,您会注意到使用BlockingQueueThread.interrupt()如何简化解决方案。

我还声称额外的stop标志是不必要的,从大图来看,可能有害。一个表现良好的工作线程应该尊重中断。意外中断只是意味着工作程序正在原始程序员没有预料到的上下文中运行。最好的事情是如果工人做了被告知要做的事情......即它应该停止......这是否符合原始程序员的概念。

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

为什么不使用您可以在需要时停止的调度程序?标准调度程序支持重复调度,它还会在重新安排新运行之前等待工作线程完成。

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

此示例将以10秒的延迟运行您的线程,这意味着当一次运行完成时,它会在10秒后重新启动它。而不是必须重新发明轮子你

service.shutdown()

while(true)不再是必要的了。

ScheduledExecutorService Javadoc

答案 3 :(得分:0)

方法1是首选方法。

只需将volatile stop字段设置为true,然后在正在运行的线程上调用interrupt()即可。这将强制所有等待以InterruptedException返回的I / O方法(如果您的库已正确写入,则将正常处理)。

答案 4 :(得分:0)

我认为你的两个案例实际上表现出相同的潜在行为。对于第二种情况,考虑线程A添加DeathEvent,之后线程B添加FooEvent。当你的作业Thread接收到DeathEvent时,它背后仍然有一个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;
  }
}

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

答案 5 :(得分:0)

我通常在包含Thread的类中放置一个标志,在我的Thread代码中我会这样做。 (注意:而不是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
        }
    }