[英]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) { ... }
...也許? 通常使用某種退出標志來控制線程運行。
不是天生的,不是。 您可以隨時使用break
或return
保釋。 只要確保你真的這樣做(在某個時候)
問題是當您的線程無關時會發生什么? 如果你只是循環檢查一個條件,你的線程將占用整個 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.