简体   繁体   English

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

[英]Java: Is `while (true) { ... }` loop in a thread bad? What's the alternative?

Is while (true) { ... } loop in threads bad? while (true) { ... }线程中的循环不好吗? What's the alternative?什么是替代方案?

Update;更新; what I'm trying to to...我想做什么...

I have ~10,000 threads, each consuming messages from their private queues.我有大约 10,000 个线程,每个线程都从它们的私有队列中使用消息。 I have one thread that's producing messages one by one and putting them in the correct consumer's queue.我有一个线程一一生成消息并将它们放入正确的消费者队列中。 Each consumer thread loops indefinitely, checking for a message to appear in their queue and process it.每个消费者线程无限循环,检查是否有消息出现在他们的队列中并处理它。

Inside Consumer.java:在 Consumer.java 中:

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

The Producer is putting messages inside Consumer message queues at a rapid pace (several million messages per second).生产者以快速的速度(每秒数百万条消息)将消息放入消费者消息队列中。 Consumers should process these messages as fast as possible!消费者应该尽快处理这些消息!

Note: the while (true) { ... } is terminated by a KILL message sent by the Producer as its last message.注意: while (true) { ... }被生产者作为最后一条消息发送的 KILL 消息终止。 However, my question is about the proper way to do this message-passing...但是,我的问题是关于执行此消息传递的正确方法......

Please see the new question , regarding this design.请参阅有关此设计的新问题

Instead of looping forever and breaking or returning, you might choose to check the interrupted status.您可以选择检查中断状态,而不是永远循环并中断或返回。

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

If your threads are tasks managed by an ExecutorService, you can have them all end gracefully simply by calling shutdownNow().如果您的线程是由 ExecutorService 管理的任务,您可以通过调用 shutdownNow() 来优雅地结束它们。

while (!stop_running) { ... }

...perhaps? ...也许? A some sort of exit flag is often used to control thread running.通常使用某种退出标志来控制线程运行。

Not inherently, no.不是天生的,不是。 You can always bail using break or return .您可以随时使用breakreturn保释。 Just make sure you actually do (at some point)只要确保你真的这样做(在某个时候)

The problem is what happens when your thread has nothing to do?问题是当您的线程无关时会发生什么? If you just loop around and around checking a condition, your thread will eat up the whole CPU doing nothing.如果你只是循环检查一个条件,你的线程将占用整个 CPU,什么也不做。 So make sure to use wait to cause your thread to block, or sleep if you don't have anything to wait on.所以一定要利用wait引起你的线程块,或sleep ,如果你没有什么要wait上。

Depends on the definition of "bad".取决于“坏”的定义。 It means that the person trying to read the code has to look elsewhere for the reason that the loop is terminated.这意味着尝试阅读代码的人必须寻找其他地方,因为循环终止。 That may make it less readable.这可能会降低它的可读性。

This mentality taken to the extreme results in the COMEFROM keyword.这种极端的心态导致 COMEFROM 关键字。 http://en.wikipedia.org/wiki/COMEFROM http://en.wikipedia.org/wiki/COMEFROM

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

Usually, you'll want to wait on a resource of some kind to do work, which hides actual threading details from you.通常,您需要wait某种资源来完成工作,这对您隐藏了实际的线程细节。 It sounds like you wanted to implement your own spinlock .听起来您想实现自己的spinlock

Here's some tutorial about locking I found I google.这是我在谷歌上找到的一些关于锁定的教程。

It's better to have the termination condition on the while (...) line, but sometimes the termination condition is something you can only test somewhere deep inside the loop.最好在while (...)行上设置终止条件,但有时终止条件只能在循环深处的某个地方进行测试。 Then that's what break is for (or exceptions).那么这就是break的用途(或例外)。 In fact maybe your thread must run forever until your program terminates (with System.exit );事实上,也许你的线程必须永远运行直到你的程序终止(使用System.exit ); then while (true) is definitely right.那么while (true)绝对是正确的。

But maybe you're asking about what should go inside the loop.但也许你在问什么应该进入循环。 You need to make sure to include some blocking operation, ie, some function call where your thread will wait for someone else (another thread, another program, the OS) to do something.您需要确保包含一些阻塞操作,即一些函数调用,您的线程将在其中等待其他人(另一个线程、另一个程序、操作系统)执行某些操作。 This is typically Condition.wait if you're programming with locks, or reading from a message queue, or reading from a file or network socket, or some other blocking I/O operation.如果您正在使用锁进行编程,或者从消息队列中读取,或者从文件或网络套接字中读取,或者其他一些阻塞 I/O 操作,那么这通常是Condition.wait

Note that sleep is generally not good enough.请注意, sleep通常不够好。 You can't know when other participants are going to do something, so there's no way to avoid waking up too often (thus burning up CPU time needlessly) or too seldom (thus not reacting to events in a timely way).您无法知道其他参与者何时会做某事,因此无法避免醒来太频繁(从而不必要地消耗 CPU 时间)或太少醒来(因此没有及时对事件做出反应)。 Always design your system so that when a thread has completed its job, it notifies whoever is waiting on that job (often with Condition.signal or by joining).始终设计您的系统,以便当线程完成其工作时,它会通知正在等待该工作的人(通常使用Condition.signal或通过加入)。

while (true) isn't bad if there is a way to exit the loop otherwise the call will run indefinitely. while (true)如果有办法退出循环也不错,否则调用将无限期运行。

For 10000 threads doing the while(true) call is bad practice...why don't you have a sleep() on the thread to allow other threads to run or an exit strategy if the thread finish running?对于 10000 个线程执行while(true)调用是不好的做法...为什么在线程上没有sleep()以允许其他线程运行或线程完成运行时的退出策略?

Although all the above answers are correct, I want suggest this one as I came across this situation myself: You can use a flag say:尽管上述所有答案都是正确的,但我想在我自己遇到这种情况时提出这个建议:您可以使用标志说:

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

Later, make sure that isRunning is set to false after you are done reading from the buffer or data file.稍后,确保在完成从缓冲区或数据文件读取后将 isRunning 设置为 false。

I usually go with a class attribute boolean called 'done', then the threads' run methods look like我通常使用名为“done”的类属性布尔值,然后线程的运行方法看起来像

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

You can then set done=true to kill the loop.然后您可以设置 done=true 来终止循环。 This can be done from inside the loop, or you can have another method that sets it, so that other threads can pull the plug.这可以从循环内部完成,或者您可以使用另一种方法来设置它,以便其他线程可以拔出插头。

It looks like you are busy waiting, assuming a standard BlockingQueue .假设标准BlockingQueue ,看起来您正忙于等待。 Use take instead of poll .使用take而不是poll

Other than that, for (;;) is nicer than while (true) , IMO.除此之外, for (;;)while (true)更好,IMO。

First up, the straight answer to this problem by Dough Lea:首先,Dough Lea 对这个问题的直接回答:

It is almost never a good idea to use bare spins waiting for values of variables.使用等待变量值的空自旋几乎从来都不是一个好主意。 Use Thread.onSpinWait, Thread.yield, and/or blocking synchronization to better cope with the fact that "eventually" can be a long time, especially when there are more threads than cores on a system.使用 Thread.onSpinWait、Thread.yield 和/或阻塞同步来更好地应对“最终”可能是很长时间的事实,尤其是当系统上的线程数多于内核数时。

http://gee.cs.oswego.edu/dl/html/j9mm.html http://gee.cs.oswego.edu/dl/html/j9mm.html

Thead.onSpinWait was introduced with Java 9. It could look like this. Thead.onSpinWait 是在 Java 9 中引入的。它看起来像这样。

while (true) {
    while (messageQueue.peek() == null) {
       Thread.onSpinWait();
    }
    // do something with the message
}

By invoking this method within each iteration of a spin-wait loop construct, the calling thread indicates to the runtime that it is busy-waiting.通过在自旋等待循环构造的每次迭代中调用此方法,调用线程向运行时指示它正在忙等待。 The runtime may take action to improve the performance of invoking spin-wait loop constructions.运行时可能会采取措施来提高调用自旋等待循环构造的性能。

https://docs.oracle.com/javase/9/docs/api/java/lang/Thread.html#onSpinWait-- https://docs.oracle.com/javase/9​​/docs/api/java/lang/Thread.html#onSpinWait--

If I were do what you are talking about I would try this:如果我按照你所说的去做,我会试试这个:

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;
                }
            }
        }
    }
}

This allows you to make sure that you don't get any concurrent modification exepction on your messageQueue, as well as when there is no message you will not be using CPU time in the while(true) loop.这使您可以确保不会在 messageQueue 上获得任何并发修改异常,并且当没有消息时,您将不会在 while(true) 循环中使用 CPU 时间。 Now you just need to make sure that when you do add something to your messageQueue you can call lock.notifyAll() so that the thread will know to run again.现在您只需要确保当您向 messageQueue 添加某些内容时,您可以调用lock.notifyAll()以便线程知道再次运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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