簡體   English   中英

如何處理:循環調用'Thread.sleep()',可能是忙等待

[英]How to deal with: Call to 'Thread.sleep()' in a loop, probably busy-waiting

伙計們如何處理這樣的代碼和警告?

private void listenOnLogForResult() {
    String logs = "";
    int timeCounter = 1;
    while (logs.isEmpty()) {
        try {
            timeCounter++;
            Thread.sleep(2000); // Wait 2 seconds
        } catch (InterruptedException e) {
            log.error(e.getLocalizedMessage(), e);
        }
        if (timeCounter < 30) {
            logs = checkLogs()
        } else {
            logs = "Time out";
        }
    }
}

我需要將當前線程暫停 2 秒以等待文件被填充,但我的 Intelij Rise 出現問題。 在此處輸入圖像描述 而且我從聲納收到錯誤:SonarLint:要么重新中斷此方法,要么重新拋出“InterruptedException”。

我已經嘗試過很多ExecutorService ,但它總是在單獨的線程中運行,我需要暫停當前線程。

請幫忙..

等待忙碌的警告

這是來自 intellij 的可疑警告,從某種意義上說,您正在做的事情通常是直接需要的。 換句話說,它正在檢測過度使用的模式,但其使用量不能減少到 0。因此,可能正確的解決方案是告訴 intellij 在這里閉嘴。

它正在查看的問題不是Thread.sleep 那不是問題。 但是,intellij 的這種模式檢測器需要它來找到這種情況,但這不是它所抱怨的,這可能有點難以理解。

IntelliJ 擔心的是,您會無緣無故地浪費周期不斷地重新檢查log.isEmpty() 這段代碼的while方面有問題,而不是 sleep 它更願意看到調用某種logs.poll()方法的代碼,該方法只會等到它被新日志出現的行為主動喚醒。

如果這一切都在單個 java 進程中運行,那么您確實可以重寫整個系統(包括重寫此處的任何log ,以及對checkLogs()方法的完全重新想象:而不是出去檢查,無論如何正在制作日志需要喚醒此代碼。

如果不是,您可能需要告訴 intellij 將其關閉:如果沒有完整的系統重新設計,您正在做的事情是不可避免的。

重新中斷警告

您在這里有一些可悲的異常處理。

您的一般異常處理

不要編寫記錄某些內容並繼續移動的catch 這是非常糟糕的錯誤處理:系統的變量和字段現在位於未知的 state 中(您剛剛捕獲並記錄了一些內容:當然這意味着您不知道發生了什么條件導致這行執行發生,)。 然而代碼會繼續前進:通常,“捕獲異常並繼續運行”風格的代碼極有可能導致更多異常。 在未知 state 上運行的代碼遲早會崩潰和燒毀。

然后,如果以相同的方式處理崩潰和燒傷(抓住它,記錄它,繼續前進),那么你會得到另一個崩潰和燒傷。 您最終得到的代碼將在遇到問題時將186 個異常打印到日志中,除了第一個異常之外,它們都完全不相關 太壞了玉玉。

您還使調用代碼完全不可能恢復。 異常的要點是它們需要無休止地向上冒泡:要么異常被實際上知道如何處理問題的代碼捕獲(並且記錄它並沒有處理它),你正在使它成為不可能,或者。 代碼異常應該一直冒泡到入口點處理程序,這是記錄錯誤和中止入口點處理程序的正確位置。

入口點處理程序是通用模塊或應用程序運行器; 開箱即用,最終調用您的psv main()方法的java.exe本身的代碼是最明顯的“入口點運行程序”,但還有更多:Web 框架最終將調用您的一些代碼處理 web 請求:您的代碼類似於psv main() :它是入口點,調用它的 web 框架中的代碼是入口點運行器。

入口點運行程序有充分的理由來catch (Throwable t) ,並主要將其捕獲塊用於記錄它,盡管它們通常應該記錄的不僅僅是異常(例如,web 處理程序應該記錄請求詳細信息,例如發送了哪個 HTTP 參數以及它是哪個路徑請求,可能是標頭等)。 但是,任何其他代碼都不應該這樣做

如果您不知道該做什么並且不想考慮該異常可能意味着什么,那么正確的“無論如何,只需編譯 javac”代碼策略就是將異常類型添加到throws行。 如果這不可行,那么 catch 塊中的正確代碼是:

} catch (ExceptionIDoNotWantToThinkAboutRightNow e) {
    throw new RuntimeException("Uncaught", e);
}

這將確保代碼不僅會愉快地繼續前進,在未知的 state 上運行,還將確保您在日志中獲得完整的詳細信息,並確保調用代碼可以捕獲並處理它,並確保任何自定義日志記錄信息,例如由於 HTTP 請求詳細信息有機會進入日志。 三贏三贏。

特別是這種情況:InterruptedEx 是什么意思?

當在 java 進程中運行的某些代碼調用yourThread.interrupt()時,這就是可能發生InterruptedException的方式,並且不可能以任何其他方式發生 如果用戶按 CTRL+C,或者進入任務管理器並單擊“結束進程”,或者如果您的 android 手機決定是時候讓您的應用退出,因為 memory 需要其他東西 -這些情況都不可能導致中斷異常。 您的線程只是被 java 中途殺死(如果您想對關機采取行動,請使用Runtime.getRuntime().addShutdownHook )。 唯一的方法是讓一些代碼調用.interrupt() ,而核心庫中的任何內容都不會這樣做。 因此,InterruptedException 意味着您認為“此線程上的 call.interrupt()”意味着什么。 它是由你決定。

最常見的定義實際上是“我請你停止”:只是很好地關閉線程。 通常,如果您想退出整個 VM,嘗試很好地關閉線程是不好的(只需調用System.shutdown - 您已經需要處理用戶按 CTRL+C,為什么要以不同的方式編寫兩次關閉代碼?) - 但是有時你只想讓一個線程停止。 因此,通常放入catch (InterruptedException e)塊的最佳代碼就是return; 沒有別的了。 不要記錄任何東西:“中斷”是故意的:你寫的。 這很可能不在您的代碼庫中,並且 InterruptedException 沒有實際意義:它永遠不會發生。

在您的特定代碼中,如果您的代碼決定停止記錄器線程會發生什么,記錄器線程會將某些內容記錄到錯誤日志中,然后將縮短其 2 秒的等待時間以立即檢查日志,然后繼續運行。 這聽起來完全沒用。

但是,它意味着任何你想要的。 如果您希望用戶能夠點擊“立即強制檢查日志”按鈕,那么您可以定義中斷日志記錄線程只是縮短 2 秒(但隨后只有一個帶有注釋的空 catch 塊,解釋該這就是你設計的方式,顯然不要記錄它)。 如果您還想要一個按鈕來“停止日志記錄線程”,請使用一個 AtomicBoolean 來跟蹤“正在運行”state:當點擊“停止日志刷新”按鈕時,將 AB 設置為“假”,然后中斷線程:然后您粘貼的代碼需要檢查AB並return; 如果它是false則關閉線程。

fun sleep(timeMillis: Long) {
    val currentTimeMillis = System.currentTimeMillis()
    while (true) {
        if (System.currentTimeMillis() - currentTimeMillis >= timeMillis) {
            break
        }
    }
}

並在你的方法中使用它(這是 koltin 的代碼,你應該轉換為 java)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM