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

发布于 2024-09-12 05:41:52 字数 750 浏览 10 评论 0原文

线程中的 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 消息作为其最后一条消息终止。但是,我的问题是关于执行此消息传递的正确方法...

请参阅关于此设计的新问题

Is while (true) { ... } loop in threads bad? What's the alternative?

Update; what I'm trying to to...

I have ~10,000 threads, each consuming messages from their private queues. 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:

@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. However, my question is about the proper way to do this message-passing...

Please see the new question, regarding this design.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

单挑你×的.吻 2024-09-19 05:41:52

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

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

如果您的线程是由 ExecutorService 管理的任务,您只需调用 shutdownNow() 即可让它们全部正常结束。

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().

只有影子陪我不离不弃 2024-09-19 05:41:52
while (!stop_running) { ... }

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

while (!stop_running) { ... }

...perhaps? A some sort of exit flag is often used to control thread running.

凉薄对峙 2024-09-19 05:41:52

不是天生的,不是。您始终可以使用 breakreturn 进行保释。只要确保你确实(在某些时候)这样做了

问题是当你的线程无事可做时会发生什么?如果你只是循环检查一个条件,你的线程就会耗尽整个 CPU 而不做任何事。因此,请确保使用 wait 来让线程阻塞,或者如果没有任何内容可以 wait 则使用 sleep

Not inherently, no. You can always bail using break or return. 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. So make sure to use wait to cause your thread to block, or sleep if you don't have anything to wait on.

素手挽清风 2024-09-19 05:41:52

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

这种心态发挥到了极致,导致了 COMEFROM 关键字的诞生。 http://en.wikipedia.org/wiki/COMEFROM

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

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. http://en.wikipedia.org/wiki/COMEFROM

10 COMEFROM 40
20 INPUT "WHAT IS YOUR NAME? "; A$
30 PRINT "HELLO, "; A$
40 REM
戈亓 2024-09-19 05:41:52

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

First up, the straight answer to this problem by 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.

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

Thead.onSpinWait was introduced with Java 9. It could look like this.

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

撕心裂肺的伤痛 2024-09-19 05:41:52

通常,您需要等待某种资源来完成工作,这会向您隐藏实际的线程详细信息。听起来您想实现自己的 spinlock

这是我在 google 上找到的一些有关锁定的教程。

Usually, you'll want to wait on a resource of some kind to do work, which hides actual threading details from you. It sounds like you wanted to implement your own spinlock.

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

迷迭香的记忆 2024-09-19 05:41:52

最好将终止条件放在 while (...) 行上,但有时终止条件只能在循环深处的某个位置进行测试。这就是 break 的用途(或例外)。事实上,也许你的线程必须永远运行,直到你的程序终止(使用System.exit);那么 while (true) 绝对是正确的。

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

请注意,睡眠通常不够。您无法知道其他参与者何时会做某事,因此无法避免醒来太频繁(从而不必要地消耗 CPU 时间)或太少醒来(因此无法及时对事件做出反应)。始终设计您的系统,以便当线程完成其作业时,它会通知正在等待该作业的任何人(通常使用 Condition.signal 或通过加入)。

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. Then that's what break is for (or exceptions). In fact maybe your thread must run forever until your program terminates (with System.exit); then while (true) is definitely right.

But maybe you're asking about what should go inside the loop. You need to make sure to include some blocking operation, i.e., 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.

Note that sleep is generally not good enough. 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). 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).

寻找我们的幸福 2024-09-19 05:41:52

如果有办法退出循环,那么 while (true) 也不错,否则调用将无限期地运行。

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

while (true) isn't bad if there is a way to exit the loop otherwise the call will run indefinitely.

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?

哑剧 2024-09-19 05:41:52

虽然以上所有答案都是正确的,但我想建议这个,因为我自己也遇到过这种情况:
您可以使用一个标志:

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

稍后,在完成从缓冲区或数据文件的读取后,确保 isRunning 设置为 false。

Although all the above answers are correct, I want to 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.

菊凝晚露 2024-09-19 05:41:52

我通常使用一个名为“done”的类属性布尔值,然后线程的运行方法看起来像

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

你可以设置done=true来终止循环。这可以从循环内部完成,或者您可以使用另一种方法来设置它,以便其他线程可以拔出插头。

I usually go with a class attribute boolean called 'done', then the threads' run methods look like

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

You can then set done=true to kill the loop. 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.

装纯掩盖桑 2024-09-19 05:41:52

假设有一个标准的 BlockingQueue,您似乎正忙于等待。使用 take 而不是 poll

除此之外,在我看来,for (;;)while (true) 更好。

It looks like you are busy waiting, assuming a standard BlockingQueue. Use take instead of poll.

Other than that, for (;;) is nicer than while (true), IMO.

沦落红尘 2024-09-19 05:41:52

如果我做你正在谈论的事情,我会尝试这样做:

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

这可以让你确保你的消息队列上没有任何并发​​修改异常,以及当没有消息时你不会使用CPU时间while(true) 循环。现在您只需要确保当您向 messageQueue 添加某些内容时,您可以调用 lock.notifyAll() 以便线程知道再次运行。

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. 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文