Java:在线程中`while(true){...}`循环不好吗?有什么选择?

时间:2010-07-29 21:43:57

标签: java multithreading concurrency loops message-passing

线程中while (true) { ... }循环不好吗?有什么替代方案?

更新;我想要的是什么......

我有大约10,000个线程,每个线程都使用来自其私有队列的消息。我有一个线程一个接一个地生成消息并将它们放在正确的消费者队列中。每个消费者线程无限循环,检查消息是否出现在队列中并进行处理。

Inside Consumer.java:

@Override
public void run() {
    while (true) {
        Message msg = messageQueue.poll();
        if (msg != null) {
            ... // do something with the message
        }
    }
}

Producer正在快速地将消息放入消费者消息队列中(每秒数百万条消息)。消费者应该尽快处理这些消息!

注意:while (true) { ... }由Producer作为最后一条消息发送的KILL消息终止。但是,我的问题是关于正确传递消息的方法......

有关此设计,请参阅the new question

12 个答案:

答案 0 :(得分:16)

您可以选择检查中断状态,而不是永远循环,中断或返回。

while (!Thread.currentThread().isInterrupted()) {
    try {
        doWork();
        wait(1000);
    } catch (InterruptedException ex) {
        Thread.currentThread().interrupt();
    }
}

如果你的线程是由ExecutorService管理的任务,你可以通过调用shutdownNow()来优雅地结束它们。

答案 1 :(得分:7)

while (!stop_running) { ... }

...也许?某种退出标志通常用于控制线程运行。

答案 2 :(得分:7)

不是天生就没有。您可以随时使用breakreturn保释。只要确保你确实(在某些时候)

问题是当你的线程无关时会发生什么?如果你只是绕着检查一个条件,你的线程将占用整个CPU什么都不做。因此,请确保使用wait导致您的帖子被阻止,或sleep如果您没有任何内容wait

答案 3 :(得分:3)

取决于“坏”的定义。这意味着尝试读取代码的人必须在其他地方查找循环终止的原因。这可能会降低其可读性。

这种心态极端导致了COMEFROM关键字。 http://en.wikipedia.org/wiki/COMEFROM

10 COMEFROM 40
20 INPUT "WHAT IS YOUR NAME? "; A$
30 PRINT "HELLO, "; A$
40 REM

答案 4 :(得分:1)

最好在while (...)行上设置终止条件,但有时终止条件是你只能在循环内部的某个地方进行测试。那就是break的用途(或例外)。事实上,你的线程必须永远运行,直到你的程序终止(System.exit);那么while (true)肯定是对的。

但也许你问的是循环内应该是什么。您需要确保包含一些阻塞操作,即一些函数调用,其中您的线程将等待其他人(另一个线程,另一个程序,操作系统)执行某些操作。如果您使用锁编程,或从消息队列中读取,或从文件或网络套接字读取,或其他阻塞I / O操作,则通常为Condition.wait

请注意,sleep通常足够好。你无法知道其他参与者什么时候会做某事,所以没有办法避免经常醒来(因此不必要地浪费CPU时间)或者很少(因此没有及时对事件作出反应)。始终设计您的系统,以便当一个线程完成其工作时,它会通知谁正在等待该工作(通常使用Condition.signal或加入)。

答案 5 :(得分:1)

尽管以上所有答案都是正确的,但当我自己遇到这种情况时,我还是建议您: 您可以使用标记说:

isRunning=true;
while(isRunning){
   //do Something
}

稍后,请确保从缓冲区或数据文件中读取完之后将isRunning设置为false。

答案 6 :(得分:0)

通常,您需要wait在某种资源上进行工作,这会隐藏您的实际线程详细信息。听起来你想要实现自己的spinlock

Here's some tutorial about locking I found I google.

答案 7 :(得分:0)

我通常使用名为'done'的类属性boolean,然后线程的run方法看起来像

done = false;
while( !done ) {
    // ... process stuff
}

然后你可以设置done = true来杀死循环。这可以在循环内部完成,或者您可以使用另一种方法来设置它,以便其他线程可以拔出插件。

答案 8 :(得分:0)

如果有办法退出循环,

while (true)也不错,否则调用将无限期地运行。

对于10000个线程进行while(true)调用是不好的做法...为什么在线程上没有sleep()允许其他线程运行或者如果线程完成运行则退出策略?

答案 9 :(得分:0)

看起来你正忙着等待,假设标准为BlockingQueue。使用take代替poll

除此之外,for (;;)比IMO while (true)更好。

答案 10 :(得分:0)

首先,是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--

答案 11 :(得分:-1)

如果我按你所说的去做,我会试试这个:

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()以便线程知道再次运行。