1. 程式人生 > >synchronized鎖住的是括號裡的物件,而不是程式碼。

synchronized鎖住的是括號裡的物件,而不是程式碼。

轉自:http://blog.csdn.net/xiao__gui/article/details/8188833

在Java中,synchronized關鍵字是用來控制執行緒同步的,就是在多執行緒的環境下,控制synchronized程式碼段不被多個執行緒同時執行。synchronized既可以加在一段程式碼上,也可以加在方法上。

關鍵是,不要認為給方法或者程式碼段加上synchronized就萬事大吉,看下面一段程式碼:

  1. class Sync {  
  2.     publicsynchronizedvoid test() {  
  3.         System.out.println("test開始.."
    );  
  4.         try {  
  5.             Thread.sleep(1000);  
  6.         } catch (InterruptedException e) {  
  7.             e.printStackTrace();  
  8.         }  
  9.         System.out.println("test結束..");  
  10.     }  
  11. }  
  12. class MyThread extends Thread {  
  13.     publicvoid run() {  
  14.         Sync sync = new Sync();  
  15.         sync.test();  
  16.     }  
  17. }  
  18. publicclass Main {  
  19.     publicstaticvoid main(String[] args) {  
  20.         for (int i = 0; i < 3; i++) {  
  21.             Thread thread = new MyThread();  
  22.             thread.start();  
  23.         }  
  24.     }  
  25. }  

執行結果:
test開始..
test開始..
test開始..
test結束..
test結束..
test結束..

可以看出來,上面的程式起了三個執行緒,同時執行Sync類中的test()方法,雖然test()方法加上了synchronized,但是還是同時執行起來,貌似synchronized沒起作用。 

將test()方法上的synchronized去掉,在方法內部加上synchronized(this):

  1. publicvoid test() {  
  2.     synchronized(this){  
  3.         System.out.println("test開始..");  
  4.         try {  
  5.             Thread.sleep(1000);  
  6.         } catch (InterruptedException e) {  
  7.             e.printStackTrace();  
  8.         }  
  9.         System.out.println("test結束..");  
  10.     }  
  11. }  

執行結果:
test開始..
test開始..
test開始..
test結束..
test結束..
test結束..

一切還是這麼平靜,沒有看到synchronized起到作用。 

實際上,synchronized(this)以及非static的synchronized方法(至於static synchronized方法請往下看),只能防止多個執行緒同時執行同一個物件的同步程式碼段。

回到本文的題目上:synchronized鎖住的是程式碼還是物件。答案是:synchronized鎖住的是括號裡的物件,而不是程式碼。對於非static的synchronized方法,鎖的就是物件本身也就是this。

當synchronized鎖住一個物件後,別的執行緒如果也想拿到這個物件的鎖,就必須等待這個執行緒執行完成釋放鎖,才能再次給物件加鎖,這樣才達到執行緒同步的目的。即使兩個不同的程式碼段,都要鎖同一個物件,那麼這兩個程式碼段也不能在多執行緒環境下同時執行。

所以我們在用synchronized關鍵字的時候,能縮小程式碼段的範圍就儘量縮小,能在程式碼段上加同步就不要再整個方法上加同步。這叫減小鎖的粒度,使程式碼更大程度的併發。原因是基於以上的思想,鎖的程式碼段太長了,別的執行緒是不是要等很久,等的花兒都謝了。當然這段是題外話,與本文核心思想並無太大關聯。

再看上面的程式碼,每個執行緒中都new了一個Sync類的物件,也就是產生了三個Sync物件,由於不是同一個物件,所以可以多執行緒同時執行synchronized方法或程式碼段。

為了驗證上述的觀點,修改一下程式碼,讓三個執行緒使用同一個Sync的物件。

  1. class MyThread extends Thread {  
  2.     private Sync sync;  
  3.     public MyThread(Sync sync) {  
  4.         this.sync = sync;  
  5.     }  
  6.     publicvoid run() {  
  7.         sync.test();  
  8.     }  
  9. }  
  10. publicclass Main {  
  11.     publicstaticvoid main(String[] args) {  
  12.         Sync sync = new Sync();  
  13.         for (int i = 0; i < 3; i++) {  
  14.             Thread thread = new MyThread(sync);  
  15.             thread.start();  
  16.         }  
  17.     }  
  18. }  

執行結果:
test開始..
test結束..
test開始..
test結束..
test開始..
test結束..

可以看到,此時的synchronized就起了作用。 

那麼,如果真的想鎖住這段程式碼,要怎麼做?也就是,如果還是最開始的那段程式碼,每個執行緒new一個Sync物件,怎麼才能讓test方法不會被多執行緒執行。 

解決也很簡單,只要鎖住同一個物件不就行了。例如,synchronized後的括號中鎖同一個固定物件,這樣就行了。這樣是沒問題,但是,比較多的做法是讓synchronized鎖這個類對應的Class物件。

  1. class Sync {  
  2.     publicvoid test() {  
  3.         synchronized (Sync.class) {  
  4.             System.out.println("test開始..");  
  5.             try {  
  6.                 Thread.sleep(1000);  
  7.             } catch (InterruptedException e) {  
  8.                 e.printStackTrace();  
  9.             }  
  10.             System.out.println("test結束..");  
  11.         }  
  12.     }  
  13. }  
  14. class MyThread extends Thread {  
  15.     publicvoid run() {  
  16.         Sync sync = new Sync();  
  17.         sync.test();  
  18.     }  
  19. }  
  20. publicclass Main {  
  21.     publicstaticvoid main(String[] args) {  
  22.         for (int i = 0; i < 3; i++) {  
  23.             Thread thread = new MyThread();  
  24.             thread.start();  
  25.         }  
  26.     }  
  27. }  

執行結果:
test開始..
test結束..
test開始..
test結束..
test開始..
test結束..

上面程式碼用synchronized(Sync.class)實現了全域性鎖的效果

最後說說static synchronized方法,static方法可以直接類名加方法名呼叫,方法中無法使用this,所以它鎖的不是this,而是類的Class物件,所以,static synchronized方法也相當於全域性鎖,相當於鎖住了程式碼段。