天天看點

線程同步總結--synchronized方法和synchronized代碼塊

閱讀前必須明白:

程式代碼的目的是操作資料,而在操作資料時有可能發生同時操作同一個資料,是以為了避免同時操作一個資料産生錯誤才出現線程同步的概念,即synchronized方法和synchronized代碼塊技術。

這裡的同一個資料包括 所有對象...

還有每一個對象都有一把鎖,synchronized就是為此對象上鎖,等到synchronized方法或synchronized代碼塊執行完就會自動解鎖,所有多線程執行相同帶synchronized的代碼時會檢查所操作對象是否上鎖,如果已經被鎖住,就阻塞等待,直到鎖此代碼段的線程執行完此代碼塊。

是以非synchronized方法不會有阻塞一說,因為它沒有synchronized,是以不會檢查對象鎖住沒有

 若想深入了解對象鎖機制,參考http://zhangjunhd.blog.51cto.com/113473/70300

synchronized方法和synchronized代碼塊的了解:

synchronized方法鎖住的是this,

synchronized(this){ },鎖住this,隻要多個線程同時通路同一個SynchronizedTest執行個體(this),就會發生不能同時通路此代碼塊

synchronized(Object){ },鎖住Object,隻要多個線程同時通路同一個Object就會發生不能同時通路此代碼塊

同步的目的就避免操作同一個資料,是以操作不同資料就不必要同步。

了解下面代碼的差別,就真正了解了同步:

public class SynchronizedTest{
	
	Person p = new Person("lin", 15);
	
	public synchronized void say(){//相當于鎖住this,效果和say3()一樣,隻要多個線程同時通路同一個SynchronizedTest執行個體(相當于this),就會發生不能同時通路此方法
		System.out.println(p.getName());
	}
	
	public void say2(){
		synchronized(p){//相當于鎖住p,隻要多個線程同時通路此代碼塊且是同一個p,就會發生不能同時通路此代碼塊鎖住
			System.out.println(p.getName());
		}
	}
	
	public void say3(){
		synchronized(this){//鎖住this,效果和say()一樣,隻要多個線程同時通路同一個SynchronizedTest執行個體(this),就會發生不能同時通路此代碼塊
			System.out.println(p.getName());
		}
	}
}
           

明白則一點,就閱讀下面:

我就偷點懶,摘抄一下别人的,其實人家和我要寫的差不多

以下摘自http://blog.csdn.net/xiao__gui/article/details/8188833

在Java中,synchronized關鍵字是用來控制線程同步的,就是在多線程的環境下,控制synchronized代碼段不被多個線程同時執行。Synchronized既可以對代碼塊使用,也可以加在整個方法上。

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

[java]   view plain copy

  1. class Sync {  
  2.     public synchronized void 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.     public void run() {  
  14.         Sync sync = new Sync();  
  15.         sync.test();  
  16.     }  
  17. }  
  18. public class Main {  
  19.     public static void 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結束..

可以看出來,上面的程式起了三個線程,同時運作test方法,雖然test方法加上了synchronized,但是還是同時運作起來,貌似synchronized沒起作用。

将test方法改成如下:

[java]   view plain copy

  1. public void 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方法請往下看),隻能防止多個線程同時執行同一個對象的這個代碼段。

為什麼會這樣呢,回到本文的題目上:Java線程同步:synchronized鎖住的是代碼還是對象。答案是:synchronized鎖住的是括号裡的對象,而不是代碼。對于非靜态的synchronized方法,鎖的就是對象本身也就是this。

當synchronized鎖住一個對象後,别的線程如果也想拿到這個對象的鎖,就必須等待這個線程執行完成釋放鎖,才能再次給對象加鎖,這樣才達到線程同步的目的。

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

再看上面的代碼,每個線程中都new了一個Sync類的對象,也就是産生了三個Sync對象,由于不是同一個對象,是以可以多線程同時運作synchronized方法或代碼段。

為了驗證上述的觀點,修改一下代碼,讓三個線程使用同一個Sync的對象。

[java]   view plain copy

  1. class MyThread extends Thread {  
  2.     private Sync sync;  
  3.     public MyThread(Sync sync) {  
  4.         this.sync = sync;  
  5.     }  
  6.     public void run() {  
  7.         sync.test();  
  8.     }  
  9. }  
  10. public class Main {  
  11.     public static void 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方法和synchronized代碼段就都起了作用。

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

解決也很簡單,隻要鎖住同一個對象不就行了。例如,synchronized後的括号中鎖一個static final對象,這樣就行了。這樣是沒問題,但是,比較多的做法是讓synchronized鎖這個類對應的Class對象。

[java]   view plain copy

  1. class Sync {  
  2.     public void 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.     public void run() {  
  16.         Sync sync = new Sync();  
  17.         sync.test();  
  18.     }  
  19. }  
  20. public class Main {  
  21.     public static void 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這個概念,是以,static synchronized方法也相當于全局鎖,相當于鎖住了代碼段。