JAVA 同步之 synchronized 修飾方法被多物件訪問是否執行緒安全?
在JAVA多執行緒程式設計中,將需要併發執行的程式碼放在Thread類的run方法裡面,然後建立多個Thread類的物件,呼叫start()方法,執行緒啟動執行。
當某段程式碼需要互斥時,可以用 synchronized 關鍵字修飾,這裡討論 synchronized 關鍵字修飾方法時,是如何互斥的。
synchronized 修飾方法時鎖定的是呼叫該方法的物件。它並不能使呼叫該方法的多個物件在執行順序上互斥。
下面舉個具體的例子說明:
Test.java 通過 implements Runnable 成為一個執行緒類,它有一個MethodSync例項變數,這樣,每當例項化一個Test物件時(相當於建立一個執行緒)就會初始化一個相應的MethodSync物件。然後在Test類的run()方法裡面呼叫 synchronized 修飾的方法。
Test.java
1 public class Test implements Runnable{ 2 private String name; 3 // private static MethodSync methodSync = new MethodSync(); 4 private MethodSync methodSync = new MethodSync(); 5 6 public Test(String name){ 7 this.name = name; 8 } 9 10 @Override 11 public void run() { 12 methodSync.method(name); 13 } 14 15 public static void main(String[] args) { 16 Thread t1 = new Thread(new Test("test 1")); 17 Thread t2 = new Thread(new Test("test 2")); 18 t1.start(); 19 t2.start(); 20 } 21 }
上面建立了二個Test類的物件,相當於啟動了兩個執行緒, 這兩個執行緒將會併發地執行它們的run方法中的程式碼。
MethodSync.java ,該類只擁有一個用來測試的 synchronized 方法
1 public class MethodSync { 2 3 /* 4 * @Task : 測試 synchronized 修飾方法時鎖定的是呼叫該方法的物件 5 * @param name 執行緒的標記名稱 6 */ 7 public synchronized void method(String name){ 8 System.out.println(name + " Start a sync method"); 9 try{ 10 Thread.sleep(300); 11 }catch(InterruptedException e){} 12 System.out.println(name + " End the sync method"); 13 } 14 }
先看執行結果:
test1 先於 test2 執行 同步方法,但是卻後於 test2 結束。這裡並沒有達到互斥的效果!原因是:MethodSync是例項變數,每次建立一個Test物件就會建立一個MethodSync物件, synchronized 只會鎖定呼叫method()方法的那個MethodSync物件,而這裡建立的兩個執行緒分別擁有兩個不同的MethodSync物件,它們呼叫method方法時就沒有互斥關係。
當把Test.java 中的MethodSync 變數 用 static 來修飾時,執行結果如下:
這裡,正確實現了同步作用。原因如下:這裡也建立了二個執行緒(Test 物件),但是每個Test物件共享MethodSync 變數,也即只有一個MethodSync 變數在兩個執行緒中執行 method方法,這樣兩個執行緒在執行到method 方法這段程式碼時就會形成互斥。