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
}