[英]Java: Is `while (true) { ... }` loop in a thread bad? What's the alternative?
while (true) { ... }
线程中的循环不好吗? 什么是替代方案?
更新; 我想做什么...
我有大约 10,000 个线程,每个线程都从它们的私有队列中使用消息。 我有一个线程一一生成消息并将它们放入正确的消费者队列中。 每个消费者线程无限循环,检查是否有消息出现在他们的队列中并处理它。
在 Consumer.java 中:
@Override
public void run() {
while (true) {
Message msg = messageQueue.poll();
if (msg != null) {
... // do something with the message
}
}
}
生产者以快速的速度(每秒数百万条消息)将消息放入消费者消息队列中。 消费者应该尽快处理这些消息!
注意: while (true) { ... }
被生产者作为最后一条消息发送的 KILL 消息终止。 但是,我的问题是关于执行此消息传递的正确方法......
请参阅有关此设计的新问题。
您可以选择检查中断状态,而不是永远循环并中断或返回。
while (!Thread.currentThread().isInterrupted()) {
try {
doWork();
wait(1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
如果您的线程是由 ExecutorService 管理的任务,您可以通过调用 shutdownNow() 来优雅地结束它们。
while (!stop_running) { ... }
...也许? 通常使用某种退出标志来控制线程运行。
不是天生的,不是。 您可以随时使用break
或return
保释。 只要确保你真的这样做(在某个时候)
问题是当您的线程无关时会发生什么? 如果你只是循环检查一个条件,你的线程将占用整个 CPU,什么也不做。 所以一定要利用wait
引起你的线程块,或sleep
,如果你没有什么要wait
上。
取决于“坏”的定义。 这意味着尝试阅读代码的人必须寻找其他地方,因为循环终止。 这可能会降低它的可读性。
这种极端的心态导致 COMEFROM 关键字。 http://en.wikipedia.org/wiki/COMEFROM
10 COMEFROM 40
20 INPUT "WHAT IS YOUR NAME? "; A$
30 PRINT "HELLO, "; A$
40 REM
通常,您需要wait
某种资源来完成工作,这对您隐藏了实际的线程细节。 听起来您想实现自己的spinlock 。
最好在while (...)
行上设置终止条件,但有时终止条件只能在循环深处的某个地方进行测试。 那么这就是break
的用途(或例外)。 事实上,也许你的线程必须永远运行直到你的程序终止(使用System.exit
); 那么while (true)
绝对是正确的。
但也许你在问什么应该进入循环。 您需要确保包含一些阻塞操作,即一些函数调用,您的线程将在其中等待其他人(另一个线程、另一个程序、操作系统)执行某些操作。 如果您正在使用锁进行编程,或者从消息队列中读取,或者从文件或网络套接字中读取,或者其他一些阻塞 I/O 操作,那么这通常是Condition.wait
。
请注意, sleep
通常不够好。 您无法知道其他参与者何时会做某事,因此无法避免醒来太频繁(从而不必要地消耗 CPU 时间)或太少醒来(因此没有及时对事件做出反应)。 始终设计您的系统,以便当线程完成其工作时,它会通知正在等待该工作的人(通常使用Condition.signal
或通过加入)。
while (true)
如果有办法退出循环也不错,否则调用将无限期运行。
对于 10000 个线程执行while(true)
调用是不好的做法...为什么在线程上没有sleep()
以允许其他线程运行或线程完成运行时的退出策略?
尽管上述所有答案都是正确的,但我想在我自己遇到这种情况时提出这个建议:您可以使用标志说:
isRunning=true;
while(isRunning){
//do Something
}
稍后,确保在完成从缓冲区或数据文件读取后将 isRunning 设置为 false。
我通常使用名为“done”的类属性布尔值,然后线程的运行方法看起来像
done = false;
while( !done ) {
// ... process stuff
}
然后您可以设置 done=true 来终止循环。 这可以从循环内部完成,或者您可以使用另一种方法来设置它,以便其他线程可以拔出插头。
假设标准BlockingQueue
,看起来您正忙于等待。 使用take
而不是poll
。
除此之外, for (;;)
比while (true)
更好,IMO。
首先,Dough Lea 对这个问题的直接回答:
使用等待变量值的空自旋几乎从来都不是一个好主意。 使用 Thread.onSpinWait、Thread.yield 和/或阻塞同步来更好地应对“最终”可能是很长时间的事实,尤其是当系统上的线程数多于内核数时。
http://gee.cs.oswego.edu/dl/html/j9mm.html
Thead.onSpinWait 是在 Java 9 中引入的。它看起来像这样。
while (true) {
while (messageQueue.peek() == null) {
Thread.onSpinWait();
}
// do something with the message
}
通过在自旋等待循环构造的每次迭代中调用此方法,调用线程向运行时指示它正在忙等待。 运行时可能会采取措施来提高调用自旋等待循环构造的性能。
https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.html#onSpinWait--
如果我按照你所说的去做,我会试试这个:
private Object lock = new Object();
public void run(){
while(true){
synchronized(lock){
Message msg = messageQueue.poll();
if (msg != null) {
... // do something with the message
}else{
try{
lock.wait();
}catch(InterruptedException e){
e.printStackTrace();
continue;
}
}
}
}
}
这使您可以确保不会在 messageQueue 上获得任何并发修改异常,并且当没有消息时,您将不会在 while(true) 循环中使用 CPU 时间。 现在您只需要确保当您向 messageQueue 添加某些内容时,您可以调用lock.notifyAll()
以便线程知道再次运行。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.