深入Thread.sleep

下一個丶奇蹟發表於2017-08-10

友情推薦:

  1. 執行緒池原理
  2. 多執行緒中斷機制
  3. head first Thread.join()

實戰分析

一直都說,Threed.sleep是不會釋放鎖,而wait是釋放鎖的(物件鎖),現理論上來分析一下啊。

由於CPU分配的每個執行緒的時間片極為短暫(一般為幾十毫秒),所以,CPU通過不停地切換執行緒執行,這樣就給程式設計師一種錯覺,以為多個執行緒是在同時執行。sleep就是正在執行的執行緒主動讓出CPU,CPU去執行其他執行緒,在sleep指定的時間過後,CPU才會回到這個執行緒上繼續往下執行,如果當前執行緒進入了同步鎖,sleep方法並不會釋放鎖,即使當前執行緒使用sleep方法讓出了CPU,但其他被同步鎖擋住了的執行緒也無法得到執行。

package thread.concurrent;

public class DeepenSleep implements Runnable {
     private int number = 10;

     public void firstMethod() throws Exception {
            synchronized (this) {
                System. out.println("in first method");
                 number += 100;
                System. out.println("+100=" + number);
           }
     }

     public void secondMethod() throws Exception {
            synchronized (this) {
                System. out.println("in second method, prepare sleep");
                 /**
                 * (休息2S,阻塞執行緒) 以驗證當前執行緒物件的機鎖被佔用時, 是否被可以訪問其他同步程式碼塊
                 */
                Thread. sleep(2000);
                System. out.println("wake up!!");
                 // this.wait(2000);
                 number *= 200;
                System. out.println("*200=" + number);
           }
     }

     @Override
     public void run() {
            try {
                System. out.println("run thread...");
                firstMethod();
           } catch (Exception e) {
                e.printStackTrace();
           }
     }

     public static void main(String[] args) throws Exception {
           DeepenSleep dt = new DeepenSleep();
           Thread thread = new Thread(dt);
           thread.start();
           System. out.println("prepare run second method");
           dt.secondMethod();
     }
}

輸出如下:

執行結果

分析:主執行緒啟動起來,因為建立執行緒等的資源消耗,所以主執行緒會先執行 dt.secondMethod(),因此會先輸出prepare run second method,其後執行secondMehtod方法(注意該方法是要先鬧到鎖物件),而該方法直接將執行緒睡眠2s(注意此處物件鎖DeepenSleep的例項物件並沒有釋放),然後執行執行緒dt的run方法,該方剛發執行dt的firstMethod,然而,該方法也是需要獲取鎖物件的,而此時他沒先不能獲取到,因為secondMehtod沒有釋放鎖(準確點講,主執行緒沒有釋放鎖);然後等了2s,主執行緒睡眠時間已過,他warkup之後,因為還擁有鎖,因此直接run secondMethod的剩下的方法,先輸出”wake up”,然後執行 number*200,執行完,主執行緒釋放掉鎖,而dt執行緒拿到鎖,執行run方法,拿到鎖,執行run方法的synchronized的剩餘方法:先輸出”in first method”,然後執行加100的操作。

我們來變一下將firstMethod的同步去掉,看輸出是什麼樣子

package thread.concurrent;

public class DeepenSleep implements Runnable {
     private int number = 10;

     public void firstMethod() throws Exception {
//         synchronized (this) {
                System. out.println( "in first method");
                 number += 100;
                System. out.println( "+100=" + number);
//         }
     }

     public void secondMethod() throws Exception {
            synchronized ( this) {
                System. out.println( "in second method, prepare sleep");
                 /**
                 * (休息2S,阻塞執行緒) 以驗證當前執行緒物件的機鎖被佔用時, 是否被可以訪問其他同步程式碼塊
                 */
                Thread. sleep(2000);
                System. out.println( "wake up!!");
                 // this.wait(2000);
                 number *= 200;
                System. out.println( "*200=" + number);
           }
     }

     @Override
     public void run() {
            try {
                System. out.println( "run thread...");
                firstMethod();
           } catch (Exception e) {
                e.printStackTrace();
           }
     }

     public static void main(String[] args) throws Exception {
           DeepenSleep dt = new DeepenSleep();
           Thread thread = new Thread(dt);
           thread.start();
           System. out.println( "prepare run second method");
           dt.secondMethod();
     }
}

輸出如下:

執行結果

分析:不同點在於,主執行緒睡眠之後,沒有釋放鎖,dt執行緒執行firstMethod並不需要鎖,因此先run firstMethod中的邏輯,先加100,然今,主執行緒睡醒之後,再執行剩下的邏輯,乘以200。

Thread.sleep(1000),1000ms後是否立即執行?

不一定,在未來的1000毫秒內,執行緒不想再參與到CPU競爭。那麼1000毫秒過去之後,這時候也許另外一個執行緒正在使用CPU,那麼這時候作業系統是不會重新分配CPU的,直到那個執行緒掛起或結束;況且,即使這個時候恰巧輪到作業系統進行CPU 分配,那麼當前執行緒也不一定就是總優先順序最高的那個,CPU還是可能被其他執行緒搶佔去。

Thread.sleep(0),是否有用?

boss:“給你睡0小時”。

coder:“你TM逗我啊”。

休眠0ms,這樣的休眠有何意義?Thread.Sleep(0)的作用,就是“觸發作業系統立刻重新進行一次CPU競爭,重新計算優先順序”。競爭的結果也許是當前執行緒仍然獲得CPU控制權,也許會換成別的執行緒獲得CPU控制權。這也是我們在大迴圈裡面經常會寫一句Thread.sleep(0) ,因為這樣就給了其他執行緒比如Paint執行緒獲得CPU控制權的權力,這樣介面就不會假死在那裡。

微信掃我^_^

這裡寫圖片描述

相關文章