不同的對象
public class Sync { public synchronized void test() { System.out.println("test start"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test end"); } }
#
public class MyThread extends Thread{ public void run() { Sync sync = new Sync(); sync.test(); } public static void main(String[] args) { for (int i = 0; i < 3; ++i) { Thread thread = new MyThread(); thread.start(); } } }
執行結果
test start test start test start test end test end test end
現象
在MyThread中,每次都new一個新的Sync對象,可以看到代碼塊test雖然被加了synchonized但是還是並行執行的,初步結論:鎖住的不是代碼塊
同一個對象
public class MyThread2 extends Thread{ public Sync sync; MyThread2(Sync sync) { this.sync = sync; } public void run() { System.out.println("hi...."); sync.test(); } public static void main(String[] args) { Sync sync = new Sync(); for (int i = 0; i < 3; ++i) { Thread thread = new MyThread2(sync); thread.start(); } }
執行結果
hi.... test start hi.... hi.... test end test start test end test start test end
現象
可以看到當他們共用一個對象的時候,synchonized起了作用,這塊代碼是串行執行的
結論
鎖住的是對象
如何在多對象的時候任然鎖住代碼?
解決也很簡單,只要鎖住同一個對象就行了。例如:synchronized后的括號中鎖同一個固定對象,這樣就行了。
這樣是沒問題,但是,比較多的做法是讓synchronized鎖這個類對應的Class對象。
public class Sync2 { public void test() { synchronized (Sync2.class) { System.out.println("test start"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("test end"); } } }
#
public class MyThread3 extends Thread{ public void run() { Sync2 sync = new Sync2(); sync.test(); } public static void main(String[] args) { for (int i = 0; i < 3; ++i) { Thread thread = new MyThread3(); thread.start(); } } }
執行結果
test start
test end
test start
test end
test start
test end
synchronized的兩大不足
- 由於我們沒辦法設置synchronized關鍵字在獲取鎖的時候等待時間,所以synchronized可能會導致線程為了加鎖而無限期地處於阻塞狀態
- 使用synchronized關鍵字等同於使用了互斥鎖,即其他線程都無法獲得鎖對象的訪問權。這種策略對於讀多寫少的應用而言是很不利的,因為即使多個讀者看似可以並發運行,但他們實際上還是串行的,並將最終導致並發性能的下降。
雖然synchronized已經作為一個關鍵字被固化在Java語言中了,但它只提供了一種相當保守的線程安全策略,且該策略開放給程序員的控制能力極弱
參考
