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