繁体   English   中英

Java:线程中的`while (true) { ... }` 循环不好吗? 什么是替代方案?

[英]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) { ... }

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

不是天生的,不是。 您可以随时使用breakreturn保释。 只要确保你真的这样做(在某个时候)

问题是当您的线程无关时会发生什么? 如果你只是循环检查一个条件,你的线程将占用整个 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM