<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                ## 07 深入Thread類—線程API精講 > 每個人的生命都是一只小船,理想是小船的風帆。 > ——張海迪 前面幾節我們都在圍繞著如何創建 Thread 和 啟動 Thread 做分析。上節我們講解了 Thread 的幾種狀態,以及狀態間的變化。有些狀態的變化是被動發生的,比如 run 方法執行完后進入 TERMINATED 狀態。不過更多時候,狀態的變化是由于主動調用了某些方法。而這些方法大多數是 Thread 類的 API。本小結,我們就來重點學習下 Thread 類暴露出來的 API。 ## sleep 方法 顧名思義,線程的 sleep 方法會使線程休眠指定的時間長度。休眠的意思是,當前邏輯執行到此不再繼續執行,而是等待指定的時間。但在這段時間內,該線程持有的 monitor 鎖(鎖在后面會講解,這里可以認為對共享資源的獨占標志)并不會被放棄。我們可以認為線程只是工作到一半休息了一會,但它所占有的資源并不會交還。這樣設計很好理解,因為線程在 sleep 的時候可能是處于同步代碼塊的中間位置,如果此時把鎖放棄,就違背了同步的語義。所以 sleep 時并不會放棄鎖,等過了 sleep 時長后,可以確保后面的邏輯還在同步執行。 ![圖片描述](https://img.mukewang.com/5d7ef1b40001079108400407.jpg) sleep 方法有兩個重載,分別是: ~~~java public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos) throws InterruptedException ~~~ 兩者的區別只是一個支持休眠時間到毫秒級,另外一個到納秒級。但其實第二個并不能真的精確到納秒級別,我們來看第二個重載方法代碼: ~~~java public static void sleep(long millis, int nanos) throws InterruptedException { if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos >= 500000 || (nanos != 0 && millis == 0)) { millis++; } sleep(millis); } ~~~ 可以清楚的看到,最終調用的還是第一個毫秒級別的 sleep 方法。而傳入的納秒會被四舍五入。如果大于 50 萬,毫秒 ++,否則納秒被省略。 ## yield 方法 yield 方法我們平時并不常用。yield 單詞的意思是讓路,在多線程中意味著本線程愿意放棄 CPU 資源,也就是可以讓出 CPU 資源。不過這只是給 CPU 一個提示,當 CPU 資源并不緊張時,則會無視 yield 提醒。如果 CPU 沒有無視 yield 提醒,那么當前 CPU 會從 RUNNING 變為 RUNNABLE 狀態,此時其它等待 CPU 的 RUNNABLE 線程,會去競爭 CPU 資源。講到這里有個問題,剛剛 yield 的線程同為 RUNNABLE 狀態,是否也會參與競爭再次獲得 CPU 資源呢?經過我大量測試,剛剛 yield 的線程是不會馬上參與競爭獲得 CPU 資源的。 我們看下面測試代碼: ~~~java public class YieldExampleClient { public static void main(String[] args) { Thread xiaoming = new Thread(() -> { for (int i = 0; i < 10; i++) { System.out.println("小明--" + i); // if (i == 2) { // Thread.yield(); // } } }); Thread jianguo = new Thread(() -> { for (int i = 0; i < 10; i++) { System.out.println("建國--" + i); } }); xiaoming.start(); jianguo.start(); } } ~~~ 可以看到啟動兩個線程打印,控制臺輸出如下: ~~~ 小明--0 小明--1 小明--2 小明--3 小明--4 小明--5 小明--6 小明--7 小明--8 小明--9 建國--0 建國--1 建國--2 建國--3 建國--4 建國--5 建國--6 建國--7 建國--8 建國--9 ~~~ 每次結果有所區別,但是一般都是小明輸出到 5 以后,建國才開始輸出。這一是因為線程啟動需要時間,另外也是因為 CPU 緊張, jianguo 線程在排隊。 我們放開小明線程注解部分,讓輸出到 xiaoming 線程輸出到 2 的時候 yield ,看看會怎么樣。輸出如下: ~~~ 小明--0 小明--1 小明--2 建國--0 建國--1 ...... ~~~ 我們看前四行,xiaoming 先獲得了 CPU 的使用權,不過在打印到 2 的時候調用了 yield 方法,提示可以讓出 CPU 的使用權,而此時 CPU 接受了提示,從而讓建國獲得了 CPU 的使用權。我嘗試建立更多的線程,多次嘗試,發現小明打印到 2 的時候,肯定會切換為其它線程打印。不過如果 CPU 資源豐富,那么會無視 yield 方法,xiaoming 也無需讓出 CPU 資源。 yield 方法為了提升線程間的交互,避免某個線程長時間過渡霸占 CPU 資源。但 yield 在實際開發中用的比較少,源碼的注解也提到這一點:“*It is rarely appropriate to use this method.*”。 ## currentThread 方法 我們前幾節中已經使用過該方法,這是一個靜態方法,用于獲取當前線程的實例。用法很簡單,如下: ~~~java Thread.currentThread(); ~~~ 拿到線程的實例后,我們還可以獲取 Thread 的 名稱: ~~~java Thread.currentThread().getName(); ~~~ 這兩個方法在之前例子中我們都使用過,也比較簡單,就不再贅述。 此外我們還可以獲取線程 ID : ~~~java Thread.currentThread().getId(); ~~~ ## setPriority 方法 此方法用于設置線程的優先級。每個線程都有自己的優先級數值,當 CPU 資源緊張的時候,優先級高的線程獲得 CPU 資源的概率會更大。請注意僅僅是概率會更大,并不意味著就一定能夠先于優先級低的獲取。這和搖車牌號一個道理,我現在中簽概率是標準的 9 倍,但搖中依然搖搖無期。而身邊卻時不時的出現第一次搖號就中的朋友。如果在 CPU 比較空閑的時候,那么優先級就沒有用了,人人都有肉吃,不需要搖號了。 優先級別高可以在大量的執行中有所體現。在大量數據的樣本中,優先級高的線程會被選中執行的次數更多。 最后我們看下 setPriority 的源碼: ~~~java public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } } ~~~ Thread 有自己的最小和最大優先級數值,范圍在 1-10。如果不在此范圍內,則會報錯。另外如果設置的 priority 超過了線程所在組的 priority ,那么只能被設置為組的最高 priority 。最后通過調用 native 方法 setPriority0 進行設置。 ## interrupt 相關方法 interrupt 的意思是打斷。調用了 interrupt 方法后,線程會怎么樣?不知道你的答案是什么。我在第一次學習 interrupt 的時候,第一感覺是讓線程中斷。其實,并不是這樣。inerrupt 方法的作用是讓可中斷方法,比如讓 sleep 中斷。也就是說其中斷的并不是線程的邏輯,中斷的是線程的阻塞。這一點在本小結一開始就要徹底搞清池,否則帶著錯誤的想法會影響學習的效果。 那么 interrupt 方法調用后,對未使用可中斷方法的線程有影響嗎?我們做個簡單的實驗,代碼如下: ~~~java public class InterruptClient { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for(int i=0; i<100 ;i++){ System.out.println("I'm doing my work"); System.out.println("I'm interrupted?"+Thread.currentThread().isInterrupted()); } }); thread.start(); Thread.sleep(1); thread.interrupt(); } } ~~~ 線程 run 方法中沒有調用可中斷方法,只是輸出**I’m doing my work**,另外還會輸出自己的中斷狀態。而主線程會 sleep 一毫秒,留時間給 thread 線程啟動,然后調用 thread 線程的 interrupt 方法。我截取其中關鍵一段輸出如下: ~~~ I'm doing my work I'm interrupted?false I'm doing my work I'm interrupted?true I'm doing my work I'm interrupted?true ~~~ 這段后面的輸出一直到結束,都在重復 “I’m doing my work I’m interrupted?true“,這說明兩個問題: 1. 調用 interrupt 方法,并不會影響可中斷方法之外的邏輯。線程不會中斷,會繼續執行。這里的中斷概念并不是指中斷線程; 2. 一旦調用了 interrupt 方法,那么線程的 interrupted 狀態會一直為 ture(沒有通過調用可中斷方法或者其他方式主動清除標識的情況下); 通過上面實現我們了解了 interrupt 方法中斷的不是線程。它中斷的其實是可中斷方法,如 sleep 。可中斷方法被中斷后,會把 interrupted 狀態歸位,改回 false 。 我們還是做個實驗,代碼如下: ~~~java public class InterruptSleepClient { public static void main(String[] args) throws InterruptedException { Thread xiaopang = new Thread(()->{ for(int i=0; i<100 ;i++){ System.out.println("I'm doing my work"); try { System.out.println("I will sleep"); Thread.sleep(1000); } catch (InterruptedException e) { System.out.println("My sleeping was interrupted"); } System.out.println("I'm interrupted?"+Thread.currentThread().isInterrupted()); } }); xiaopang.start(); Thread.sleep(1); xiaopang.interrupt(); } } ~~~ 這次干活的小胖比較懶,每次干完活都要休息一秒鐘。有一次被讓他干活的老師發現,把他叫醒了。但是后來看他照睡不誤,也就隨他去了。這段代碼執行結果如下: ~~~ I'm doing my work I will sleep My sleeping was interrupted I'm interrupted? false I'm doing my work I will sleep I'm interrupted? false I'm doing my work I will sleep I'm interrupted? false ~~~ 可以看到當 xiaopang.interrupt () 執行后,睡眠中的 xiaopang 被喚醒了。這里額外需要注意的是,此時 xiaopang 線程的 interrupted 狀態還是 false 。因為可中斷線程會捕獲中斷的信號,并且會清除掉 interrupted 標識。因此輸出的 “I’m interrupted ?” 全部是 false 。 最后我們再看一下靜態方法 interrupted 。這個方法其實和成員方法 isInterrupted 方法類似,都是返回了 interrupted 狀態。不同就是 interrupted 方法返回狀態后,如果為 true 則會清除掉狀態。而 isInterrupted 則不會。上面第一段測試代碼已經驗證了這一點,被打斷后,調用 isInterrupted 一直返回 true。 下面我們來驗證下 interrupted 是否會清除標識位。把第一段代碼稍微改一下: ~~~java public class InterruptedClient { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(()->{ for(int i=0; i<100 ;i++){ System.out.println("I'm doing my work"); //原代碼 System.out.println("I'm interrupted?"+Thread.currentThread().isInterrupted()); System.out.println("I'm interrupted?"+Thread.interrupted()); } }); thread.start(); Thread.sleep(1); thread.interrupt(); } } ~~~ 改動已經在注解中說明,僅僅是改了獲取 interrupted 狀態的方法。但輸出結果卻是不一樣的: ~~~ I'm doing my work I'm interrupted?false I'm doing my work I'm interrupted?false I'm doing my work I'm interrupted?true I'm doing my work I'm interrupted?false I'm doing my work I'm interrupted?false ~~~ 可以看到在輸出 “I’m interrupted?true” 后,中斷狀態又變回了 false。 通過以上講解,可以看出 interrupt 方法只是設置了中斷標識位,這個標識位只對可中斷方法會產生作用。不過我們還可以利用它做更多的事情,比如說如果線程的 run 方法中這么寫: ~~~java while(!isInterrupted()){ //do somenting } ~~~ 這樣主線程中可以通過調用此線程的 interrupt 方法,讓其推出運行。此時 interrupted 的含義就真的是線程退出了。不過假如你的 while 循環中調用了可中斷方法,那么就會有干擾。 ## join 方法 最后我們再講解一個重要的方法 join。這個方法功能強大,也很實用。我們用它能夠實現并行化處理。比如主線程需要做兩件沒有相互依賴的事情,那么可以起 A、B 兩個線程分別去做。通過調用 A、B 的 join 方法,讓主線程 block 住,直到 A、B 線程的工作全部完成,才繼續走下去。我們來看下面這段代碼: ~~~java public class JoinClient { public static void main(String[] args) throws InterruptedException { Thread backendDev = createWorker("backed dev", "backend coding"); Thread frontendDev = createWorker("frontend dev", "frontend coding"); Thread tester = createWorker("tester", "testing"); backendDev.start(); frontendDev.start(); // backendDev.join(); // frontendDev.join(); tester.start(); } public static Thread createWorker(String role, String work) { return new Thread(() -> { System.out.println("I finished " + work + " as a " + role); }); } } ~~~ 這段代碼中,我們把 join 方法去掉。執行結果如下: ~~~ I finished backend coding as a backed dev I finished testing as a tester I finished backend coding as a frontend dev ~~~ 我們期望的是前端和后端開發完成工作后,測試才開始測試。但從輸出結果看并非如此。要想實現這個需求,我們只需把注釋打開,讓**backendDev**和**frontendDev**先做 join 操作,此時主線程會被 block 住。直到**backendDev**和**frontendDev**線程都執行結束,才會繼續往下執行。輸出如下: ~~~ I finished backend coding as a backed dev I finished frontend coding as a frontend dev I finished testing as a tester ~~~ 可以看到現在的輸出完全符合我們的期望。可見調用 join 方法后 block 的并不是被調用的**backendDev**或**frontendDev 線程**,而是調用方線程,這個需要牢記。 ## 總結 本小結講解了 Thread 的幾個常用的方法,這些方法在我們實際開發中會經常用到的,需要我們認真學習和理解。有些已經被棄用的方法沒有再講解,比如 stop 方法。關于更多的方法,其實讀者可以直接閱讀 Thread 源代碼,Thread 類的注解寫得相當詳細。其實很多時候我們自己動手直接閱讀源代碼和注解,是更為快捷的學習方式,而且也更為權威。 下一節我們繼續講解線程的相關操作 wait ()、notify ()、notifyAll ()。這些方法也會改變線程的狀態,但并不是 Thread 的 API 。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看