簡體   English   中英

Java 線程優先級沒有影響

[英]Java Thread priority has no effect

這是一個關於線程優先級的測試。 代碼來自 Thinking in Java p.809

import java.util.concurrent.*;

public class SimplePriorities implements Runnable {
    private int countDown = 5;
    private volatile double d; // No optimization
    private int priority;

    public SimplePriorities(int priority) {
        this.priority = priority;
    }

    public String toString() {
        return Thread.currentThread() + ": " + countDown;
    }

    public void run() {
        Thread.currentThread().setPriority(priority);
        while (true) {
            // An expensive, interruptable operation:
            for (int i = 1; i < 10000000; i++) {
                d += (Math.PI + Math.E) / (double) i;
                if (i % 1000 == 0)
                    Thread.yield();
            }
            System.out.println(this);
            if (--countDown == 0)
                return;
        }
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++)
            exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));
        exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
        exec.shutdown();
    }
}

我想知道為什么我不能得到像這樣的常規結果:

Thread[pool-1-thread-6,10,main]: 5 
Thread[pool-1-thread-6,10,main]: 4 
Thread[pool-1-thread-6,10,main]: 3 
Thread[pool-1-thread-6,10,main]: 2 
Thread[pool-1-thread-6,10,main]: 1 
Thread[pool-1-thread-3,1,main]: 5 
Thread[pool-1-thread-2,1,main]: 5 
Thread[pool-1-thread-1,1,main]: 5 
Thread[pool-1-thread-5,1,main]: 5 
Thread[pool-1-thread-4,1,main]: 5 
...

但隨機結果(每次我運行它都會改變):

Thread[pool-1-thread-2,1,main]: 5
Thread[pool-1-thread-3,1,main]: 5
Thread[pool-1-thread-4,1,main]: 5
Thread[pool-1-thread-2,1,main]: 4
Thread[pool-1-thread-3,1,main]: 4
Thread[pool-1-thread-1,1,main]: 5
Thread[pool-1-thread-6,10,main]: 5
Thread[pool-1-thread-5,1,main]: 5
...

我使用 i3-2350M 2C4T CPU 和 Win 7 64 位 JDK 7。有關系嗎?

Java 線程優先級沒有影響

線程優先級是高度特定於操作系統的,並且在許多操作系統上通常影響很小。 優先級有助於僅對運行隊列中的線程進行排序,並且不會改變線程以任何主要方式運行的頻率,除非您在每個線程中都使用大量 CPU。

您的程序看起來使用了大量 CPU,但除非您的內核數少於線程數,否則通過設置線程優先級,您可能看不到輸出順序的任何變化。 如果有空閑的 CPU,那么即使是優先級較低的線程也會被調度運行。

此外,線程永遠不會被餓死。 在這種情況下,即使是優先級較低的線程也會給予時間經常運行。 您應該看到較高優先級的線程被分配了時間片以更頻繁地運行,但這並不意味着較低優先級的線程會在運行之前等待它們完成。

即使優先級確實有助於為一個線程提供比其他線程更多的 CPU,線程程序也會受到競爭條件的影響,這有助於為其執行注入大量隨機性。 然而,您應該看到的是,最大優先級線程比其他線程更有可能更頻繁地吐出它的0消息。 如果您將優先級添加到println() ,那么在多次運行后應該會變得很明顯。

同樣重要的是要注意System.out.println(...)synchronized方法,它正在寫入 IO,這將極大地影響線程的交互方式和不同的線程相互阻塞的方式。 另外, Thread.yield(); 可以是空操作,具體取決於操作系統如何進行線程調度。

但隨機結果(每次我運行它都會改變):

對。 線程程序的輸出很少是“完美的”,因為根據定義,線程是異步運行的。 我們希望輸出是隨機的,因為我們希望線程彼此獨立地並行運行。 那就是他們的力量。 如果您期待一些精確的輸出,那么您不應該使用線程。

線程優先級取決於實現。 特別是在 Windows 中:

當所有線程都在爭奪 CPU 時,線程優先級意義不大。 來源

《Java並發實踐》一書也說

避免使用線程優先級的誘惑,因為它們會增加平台依賴性並可能導致活性問題。 大多數並發應用程序可以對所有線程使用默認優先級。

線程優先級不保證執行順序。 當資源有限時,它就會發揮作用。 如果系統由於內存或 CPU 而運行受限,則優先級較高的線程將首先運行。 假設您有足夠的系統資源(對於一個簡單的程序和您發布的系統資源,我會假設如此),那么您將沒有任何系統限制。 這是我發現的一篇博客文章(不是我的文章),提供了有關它的更多信息: Why Thread Priority Rarely Matters

讓我們保持簡單,直奔源頭......

每個線程都有一個優先級。 當存在對處理資源的競爭時,優先級較高的線程通常優先於優先級較低的線程執行。 然而,這種偏好並不能保證最高優先級的線程將始終運行,並且線程優先級不能用於可靠地實現互斥。

  • 來自Java 語言規范(第 2 版)第445 頁。

還 ...

盡管 Java 中存在線程優先級並且許多參考文獻指出 JVM 將始終選擇優先級最高的線程之一進行調度 [52, 56, 89],但目前 Java 語言或虛擬機規范無法保證這一點 [53, 90] . 優先級只是對調度程序的提示[127,第 227 頁]。

  • 來自測試並發 Java 組件(博士論文,2005 年) p。 62.

  • 參考文獻 127,第 227 頁(來自上面的摘錄)來自Component Software: Beyond Object-Oriented Programming(C. Szyperski 着),Addison Wesley,1998 年。

簡而言之,不要依賴線程優先級

線程優先級只是對操作系統任務調度程序的一個提示。 任務調度器只會嘗試為具有更高優先級的線程分配更多資源,但是沒有明確的保證。

事實上,它不僅與Java或JVM有關。 大多數非實時操作系統僅以提示方式使用線程優先級(托管或非托管)。

Jeff Atwood 有一篇關於這個主題的好帖子:“ 線程優先級是邪惡的

這就是問題所在。 如果有人開始在較低優先級的線程(生產者)上使 'cond' 為真,然后程序的時間安排使得發出此旋轉的較高優先級線程(消費者)得到調度,消費者將完全餓死生產者。 這是一場經典的比賽。 即使那里有一個顯式的 Sleep,發出它也不允許對生產者進行調度,因為它的優先級較低。 消費者將永遠旋轉,除非有空閑的 CPU 開放,否則生產者將永遠不會生產。 哎呀!

正如其他答案中提到的,線程優先級與其說是嚴格規則的定義,不如說是一種提示。

也就是說,即使以嚴格(更)的方式考慮優先級,您的測試設置也不會導致您描述為“預期”的結果。 您首先創建 5 個具有同等低優先級的線程和一個具有高優先級的線程。

  • 您使用的 CPU (i3) 有 4 個本機線程。 因此,即使高優先級意味着線程不間斷運行(這是不正確的),低優先級線程也將獲得 3/4 的 CPU 功率(假設沒有其他任務正在運行)。 該 CPU 功率分配給 5 個線程,因此低優先級線程將以 4 * 3/4 * 1/5 = 高優先級線程速度的 3/5 倍運行。 高優先級線程完成后,低優先級線程以高優先級線程速度的 4/5 運行。

  • 您在高優先級線程之前啟動低優先級線程。 因此他們開始得更早一些。 我希望在大多數系統中,“優先級”並未實現到納秒級。 因此操作系統將允許一個線程運行“更長的時間”,直到它切換到另一個線程(以減少切換成本的影響)。 因此,結果在很大程度上取決於切換的實現方式以及任務的大小。 如果任務很小,則可能不會發生切換,並且在您的示例中,所有低優先級線程首先完成。

  • 這些計算假定高優先級和低優先級被解釋為極端情況。 事實上,優先級更像是帶有變量 n 和 m 的“在 m 種情況下的 n 種情況下更喜歡這個”。

  • 你的代碼中有一個 Thread.yield ! 這會將執行傳遞給另一個線程。 如果您經常這樣做,將導致所有線程獲得相同的 CPU 功率。

因此,我不希望您在問題中提到的輸出,即高優先級線程首先完成,然后低優先級線程完成。

確實:使用 Thread.yield 行,我得出的結果是每個線程獲得相同的 CPU 時間。 如果沒有 Thread.yield 行並且將計算次數增加 10 倍並將低優先級線程的數量增加 10 倍,我得到了預期的結果,即高優先級線程提前完成了某個因子(這取決於本機 CPU 線程的數量)。

我相信操作系統可以隨意忽略 Java 線程優先級。

線程優先級是一個提示(可以忽略),當您擁有 100% CPU 時,操作系統知道您希望某些線程優先於其他線程。 線程優先級必須在線程啟動之前設置,否則可以忽略。

在 Windows 上,您必須是管理員才能設置線程優先級。

需要考慮的幾件事:

某些操作系統沒有為線程優先級提供適當的支持。

您必須了解不同的操作系統以不同的方式處理線程優先級。 例如,Windows 使用基於搶占式時間片的調度程序,它為更高優先級的線程提供更大的時間片,而其他一些操作系統(非常老的操作系統)使用非搶占式調度程序,其中更高優先級的線程完全在低優先級線程之前執行除非更高優先級的線程進入睡眠狀態或進行一些 IO 操作等。

這就是為什么不能保證高優先級線程完全執行的原因,它實際上比低優先級線程執行的時間更長,這就是為什么您的輸出以這種方式排序的原因。

要了解 Windows 如何處理多線程,請參閱以下鏈接:

https://docs.microsoft.com/en-us/windows/win32/procthread/multitasking

高優先級線程在完成之前不會停止低優先級線程。 高優先級也不會使某些事情變得更快,如果確實如此,我們將始終將所有事情都設為高優先級。 低優先級不會讓事情變慢,否則我們永遠不會使用它。

據我了解,您誤解了系統的其余部分應該空閑,只讓最高優先級的線程工作,而系統的其余部分則被浪費了。

暫無
暫無

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

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