synchronized 是 java 多線程編程中用於使線程之間的操作串行化的關鍵字。這種措施類似於數據庫中使用排他鎖實現並發控制,但是有所不同的是,數據庫中是對數據對象加鎖,而 java 則是對將要執行的代碼加鎖。
在 java 中使用 synchronized 關鍵字時需要注意以下幾點:
1.synchronized 是針對同一個資源的訪問作出限制。這是出現該關鍵字的原因。
2.對於類而言,某一個線程執行到一個 synchronized 修飾的類方法或者類方法中的代碼段時,該方法或者代碼段就被加上互斥鎖,同時該類中的所有使用 synchronized 修飾的類方法或者類方法中的代碼段都被加上互斥鎖,但不影響沒有使用 synchronized 修飾的類方法或者類方法中的代碼段,同時也不影響該線程執行這些剩余已經被加上鎖的類方法或者類方法中的代碼段以及其他類中的任意方法;當線程執行完被 synchronized 修飾的類方法或者類方法中的代碼段後,這些互斥鎖都將同時被釋放。
3.對於對象而言,某一個線程執行到一個 synchronized 修飾的對象方法或者對象方法中的代碼段時,情況和 2 中一樣。
4. 第 3 點和第 2 點相互之間沒有影響
5.synchronized() 括號內所使用的內容指示將要封鎖的資源是一個類還是一個對象中的方法或者代碼塊。由於無法對類聲明中使用 synchronized 修飾符,所以對於類而言,需要使用類似於 synchronized(ClassName.class){/*...*/} 形式或者使用 synchronized 修飾類方法
實例代碼如下:
(1)Printer 類
1 public class Printer { 2 public static void printA() 3 { 4 System.out.println(Thread.currentThread() + ": invoke printA method"); 5 synchronized(Printer.class) 6 { 7 for(int i = 0; i < 5; i++) 8 { 9 System.out.println(Thread.currentThread() + ": into printA method's loop..."); 10 try 11 { 12 Thread.sleep(1000); 13 } 14 catch(InterruptedException e) 15 { 16 e.printStackTrace(); 17 } 18 } 19 } 20 } 21 22 public synchronized void printB() 23 { 24 System.out.println(Thread.currentThread() + ": invoke printB method"); 25 for(int i = 0; i < 5; i++) 26 { 27 System.out.println(Thread.currentThread() + ": into printB method's loop..."); 28 try 29 { 30 Thread.sleep(1000); 31 } 32 catch(InterruptedException e) 33 { 34 e.printStackTrace(); 35 } 36 } 37 } 38 } View Code(2)ThreadA 類
1 package demo; 2 3 public class ThreadA extends Thread{ 4 Printer p = null; 5 public ThreadA(Printer p) 6 { 7 super("ThreadA"); 8 this.p = p; 9 } 10 public void run() 11 { 12 //p.printA(); 13 Printer.printA(); 14 } 15 } View Code(3)ThreadB 類
1 package demo; 2 3 public class ThreadB extends Thread{ 4 Printer p = null; 5 public ThreadB(Printer p) 6 { 7 super("ThreadB"); 8 this.p = p; 9 } 10 public void run() 11 { 12 //p.printB(); 13 Printer.printB(); 14 } 15 } View Code(4)MainThread 類
1 package demo; 2 3 public class MainThread { 4 public static void main(String[] args) 5 { 6 Printer p = new Printer(); 7 ThreadA ta = new ThreadA(p); 8 ThreadB tb = new ThreadB(p); 9 10 ta.start(); 11 try { 12 ta.join(10); 13 } catch (InterruptedException e) { 14 // TODO Auto-generated catch block 15 e.printStackTrace(); 16 } 17 tb.start(); 18 } 19 } View Code運行結果如下:
Thread[ThreadA,5,main]: invoke printA method
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadB,5,main]: invoke printB method
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
從上述結果可以看出,ThreadA 首先調用 run 方法,並且進入了 printA 方法的 for 循環塊,因此先獲得了互斥鎖;根據 2 即可得知 ThreadB 無法執行 printB 方法。在 MainThread 中加上 join(10) 為了給 ThreadA 更多的時間以便於讓它能先進入 printA 方法(這是因為 ThreadA 需要在執行完一條 pirnt 語句之後才能獲得互斥鎖)。如果去掉 join 語句即可驗證該點。
在上述的 Printer 類添加一個 printC 方法:
1 public void printC() 2 { 3 System.out.println(Thread.currentThread() + ": invoke printC method"); 4 } View Code創建一個 ThreadC 類:
1 package demo; 2 3 public class ThreadC extends Thread{ 4 Printer p = null; 5 public ThreadC(Printer p) 6 { 7 this.p = p; 8 } 9 public void run() 10 { 11 for(int i = 0; i < 5; i++) 12 { 13 p.printC(); 14 } 15 } 16 } View Code然後在 MainThread 中去掉 join 語句,激活線程 ThreadC,運行結果如下(各位測試時結果可能有所不同):
Thread[Thread-0,5,main]: invoke printC method
Thread[ThreadA,5,main]: invoke printA method
Thread[Thread-0,5,main]: invoke printC method
Thread[Thread-0,5,main]: invoke printC method
Thread[ThreadB,5,main]: invoke printB method
Thread[ThreadB,5,main]: into printB method's loop...
Thread[Thread-0,5,main]: invoke printC method
Thread[Thread-0,5,main]: invoke printC method
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
對於上述結果,大家可以結合 2 自己進行解釋,對於 2 中剩余的觀點以及對於對象的情況,也可以自行測試驗證。下面驗證 4 。
將 Printer 方法中的 printB 方法聲明中的 static 關鍵字去掉,在 ThreadB 類 run 方法中使用 p.printB() 替換 Printer.printB() ,運行結果如下:
Thread[ThreadA,5,main]: invoke printA method
Thread[ThreadB,5,main]: invoke printB method
Thread[ThreadB,5,main]: into printB method's loop...
Thread[Thread-0,5,main]: invoke printC method
Thread[ThreadA,5,main]: into printA method's loop...
Thread[Thread-0,5,main]: invoke printC method
Thread[Thread-0,5,main]: invoke printC method
Thread[Thread-0,5,main]: invoke printC method
Thread[Thread-0,5,main]: invoke printC method
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
Thread[ThreadB,5,main]: into printB method's loop...
Thread[ThreadA,5,main]: into printA method's loop...
對於輸出結果,各位看官可以細心分析分析
(未完待續。。。;-))
本文有參考:Devin Zhang 的博客