Java:线程中的'while(true){…}'循环是坏的吗?什么';还有什么选择?
线程中的Java:线程中的'while(true){…}'循环是坏的吗?什么';还有什么选择?,java,multithreading,concurrency,loops,message-passing,Java,Multithreading,Concurrency,Loops,Message Passing,线程中的while(true){…}循环是否错误?替代方案是什么 更新;我想说的是… 我有大约10000个线程,每个线程都使用来自其专用队列的消息。我有一个线程,它一个接一个地生成消息,并将它们放入正确的消费者队列中。每个使用者线程无限期地循环,检查队列中是否出现消息并对其进行处理 在Consumer.java内部: @Override public void run() { while (true) { Message msg = messageQueue.poll()
while(true){…}
循环是否错误?替代方案是什么
更新;我想说的是…
我有大约10000个线程,每个线程都使用来自其专用队列的消息。我有一个线程,它一个接一个地生成消息,并将它们放入正确的消费者队列中。每个使用者线程无限期地循环,检查队列中是否出现消息并对其进行处理
在Consumer.java内部:
@Override
public void run() {
while (true) {
Message msg = messageQueue.poll();
if (msg != null) {
... // do something with the message
}
}
}
生产者正在以快速的速度(每秒数百万条消息)将消息放入消费者消息队列中。消费者应该尽快处理这些信息
注意:while(true){…}
由生产者作为其最后一条消息发送的KILL消息终止。然而,我的问题是关于如何正确传递此消息
关于此设计,请参见。通常,您希望
等待某种资源来完成工作,这会对您隐藏实际的线程细节。听起来你想实现你自己的
……也许吧?某种类型的退出标志通常用于控制线程运行。不是固有的,不是。您可以始终使用中断
或返回
来退出。只要确保你真的做到了(在某个时候)
问题是当你的线程无所事事时会发生什么?如果你只是循环检查一个条件,你的线程将消耗掉整个CPU。因此,确保使用wait
使线程阻塞,或者如果没有任何东西可以wait
则使用sleep
。取决于“坏”的定义。这意味着试图读取代码的人必须在别处查找循环终止的原因。这可能会降低它的可读性
这种心态在COMEFROM关键字中走到了极端
最好在while(…)
行中设置终止条件,但有时终止条件只能在循环的深处进行测试。这就是break
的作用(或例外)。事实上,您的线程可能必须永远运行,直到程序终止(使用System.exit
);那么while(true)
绝对正确
但也许你在问什么应该进入循环。您需要确保包含一些阻塞操作,即一些函数调用,其中您的线程将等待其他人(另一个线程、另一个程序、操作系统)执行某些操作。这通常是条件。如果正在使用锁编程,或正在从消息队列读取,或正在从文件或网络套接字读取,或正在执行某些其他阻塞I/O操作,请等待
请注意,睡眠
通常不够好。您无法知道其他参与者何时要做某事,因此无法避免醒得太频繁(从而不必要地消耗CPU时间)或太少(从而无法及时对事件做出反应)。始终设计您的系统,以便当线程完成其作业时,它通知正在等待该作业的人(通常带有条件。信号或加入)。您可以选择检查中断状态,而不是永远循环并中断或返回
while (!Thread.currentThread().isInterrupted()) {
try {
doWork();
wait(1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
如果您的线程是由ExecutorService管理的任务,则只需调用Shutdownow(),它们就可以正常结束。我通常使用名为“done”的类属性布尔值,那么线程的运行方法如下所示
done = false;
while( !done ) {
// ... process stuff
}
然后可以设置done=true以终止循环。这可以从循环内部完成,或者您可以使用另一种方法来设置它,以便其他线程可以拔掉插头。而(true)
如果有退出循环的方法,则不坏,否则调用将无限期运行
对于10000个线程来说,执行while(true)
调用是一种不好的做法……为什么不在线程上设置一个sleep()
,以允许其他线程运行,或者在线程运行完后设置一个退出策略?如果我按照您所说的做,我会尝试以下方法:
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()
,这样线程就可以知道如何再次运行。假设使用标准的阻塞队列,您似乎正忙着等待。使用take
而不是poll
除此之外,for(;;)
要比好,而(true)
,IMO.首先,我要给出一个关于这个问题的直接答案:
使用裸自旋等待变量值几乎从来都不是一个好主意。使用Thread.onSpinWait、Thread.yield和/或阻塞同步来更好地处理“最终”可能需要很长时间的事实,特别是当系统上的线程数超过内核数时
Java 9引入了ad.onSpinWait。它可能是这样的
while (true) {
while (messageQueue.peek() == null) {
Thread.onSpinWait();
}
// do something with the message
}
通过在自旋等待循环构造的每次迭代中调用此方法,调用线程向运行时指示它正忙着等待。运行时可以采取措施来提高调用自旋等待循环构造的性能
尽管上述所有答案都是正确的,但我还是想提出以下建议,因为我自己也遇到过这种情况:
您可以使用一个标志来表示:
isRunning=true;
while(isRunning){
//do Something
}
稍后,在完成从缓冲区或数据文件的读取后,请确保将isRunning设置为false。为什么不详细说明您要实现的目标,我们会告诉您是否有更好的方法。@Ryan:
更新;请看一看
while (true) {
while (messageQueue.peek() == null) {
Thread.onSpinWait();
}
// do something with the message
}
isRunning=true;
while(isRunning){
//do Something
}