在Java中停止循环线程
我正在使用一个不断从队列中读取的线程。
就像是:
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()
,我可以实现一个使用primefaces检查条件变量的stopThisThread()
方法。
2 – 将死亡事件对象或类似内容发送到队列。 当线程获取死亡事件时,它将退出。
我更喜欢第一种方式,但是,我不知道何时调用stopThisThread()
方法,因为它可能在队列的路上,并且停止信号可以首先到达(不可取)。
有什么建议么?
如果您需要在关闭之前完成队列中的所有工作, DeathEvent
(或者通常称为“毒丸”)方法很有效。 问题是这可能需要很长时间。
如果你想尽快停止,我建议你这样做
BlockingQueue 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. } }
要停止使用该run
方法实例化的线程t
,只需调用t.interrupt();
。
如果将上面的代码与其他答案进行比较,您会注意到如何使用BlockingQueue
和Thread.interrupt()
简化解决方案。
我还声称不需要额外的stop
标志,从大局来看,可能有害。 一个表现良好的工作线程应该尊重中断。 意外中断只是意味着工作程序正在原始程序员没有预料到的上下文中运行。 最好的事情是如果工人做了被告知要做的事情……即它应该停止……这是否符合原始程序员的构想。
为什么不使用您可以在需要时停止的调度程序? 标准调度程序支持重复调度,该调度还在重新安排新运行之前等待工作线程完成。
ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); service.scheduleWithFixedDelay(myThread, 1, 10, TimeUnit.SECONDS);
此示例将以10秒的延迟运行您的线程,这意味着当一次运行完成时,它会在10秒后重新启动它。 而不是必须重新发明轮子你得到
service.shutdown()
while(true)不再是必要的了。
ScheduledExecutorService Javadoc
在你的读者线程中有一个布尔变量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. } }
方法1是优选的方法。
只需将volatile stop
字段设置为true,并在正在运行的线程上调用interrupt()
。 这将强制任何等待以InterruptedException
返回的I / O方法(如果您的库被正确写入,这将被正常处理)。
我认为你的两个案例实际上表现出相同的潜在行为。 对于第二种情况,考虑线程A添加DeathEvent,之后线程B添加FooEvent。 当你的作业Thread接收到DeathEvent时,它后面仍然有一个FooEvent,这与你在选项1中描述的场景相同,除非你在返回之前尝试清除队列,但是你基本上保持线程活着,当你试图做的就是阻止它。
我同意你的看法,第一种选择更令人满意。 潜在的解决方案取决于您的队列的填充方式。 如果它是你的工作线程类的一部分,你可以让你的stopThisThread()方法设置一个标志,该标志将从排队调用中返回一个适当的值(或抛出exception),即:
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; } }
然后,尝试将事件排入队列以适当地处理它的对象将负责,但至少它将知道事件不在队列中,并且不会被处理。
我通常在其中包含Thread的类中放置一个标志,在我的Thread代码中我会这样做。 (注意:而不是while(true)我做的同时(旗帜))
然后在类中创建一个方法,将标志设置为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 } }