使用線程同步解決多線程安全問題html
上一篇 Java基礎-多線程-②多線程的安全問題 中咱們說到多線程可能引起的安全問題,緣由在於多個線程共享了數據,且一個線程在操做(多爲寫操做)數據的過程當中,另外一個線程也對數據進行了操做,從而致使數據出錯。由此咱們想到一個解決的思路:將操做共享數據的代碼行做爲一個總體,同一時間只容許一個線程執行,執行過程當中其餘線程不能參與執行。線程同步就是用來實現這樣的機制。express
synchronized代碼塊安全
Java中提供了synchronized關鍵字,將可能引起安全問題的代碼包裹在synchronized代碼塊中,表示這些代碼須要進行線程同步。synchronized代碼塊的語法格式爲:多線程
1 synchronized (expression){ 2 //須要同步的代碼 3 }
其中,expression必須是一個引用類型的變量,這裏咱們能夠理解爲任意的一個Java對象,不然編譯出錯。下面的例子中咱們使用了一個Object對象obj。ide
1 class Dog implements Runnable { 2 private int t = 100; 3 private Object obj = new Object(); 4 5 @Override 6 public void run() { 7 while (true) { 8 9 synchronized (obj) { 10 if (t > 0) { 11 try { 12 Thread.sleep(100); 13 System.out.println("當前線程:" + Thread.currentThread().getName() + "---" + t--); 14 } catch (InterruptedException e) {} 15 } 16 } 17 18 } 19 } 20 }
這時候,一個線程在執行完整個代碼塊(或者非正常結束)以後,其餘的線程纔有機會進入代碼塊執行,就不會出現「打印的t小於1」的狀況了,簡單的實現了代碼的同步。post
線程同步的機制和同步鎖this
上面線程同步的效果是怎麼實現的呢?Java中任意的對象均可以做爲一個監聽器(monitor),監聽器能夠被上鎖和解鎖,在線程同步中稱爲同步鎖,且同步鎖在同一時間只能被一個線程所持有。上面的obj對象就是一個同步鎖,分析一下上面代碼的執行過程:spa
因此,一個線程執行代碼塊時,持有了同步鎖,其餘線程就不能獲取到鎖,也就不能進入代碼塊執行,只能等待鎖被釋放。這時候咱們思考這樣一個問題:在synchronized代碼塊中若是咱們每次傳入的都是一個新的對象,可否實現同步的效果呢?以下:線程
1 public void run() { 2 while (true) { 3 synchronized (new Object()) { 4 if (t > 0) { 5 try { 6 Thread.sleep(100); 7 System.out.println("當前線程:" + Thread.currentThread().getName() + "---" + t--); 8 } catch (InterruptedException e) {} 9 } 10 } 11 } 12 }
顯然多個線程檢查的都是一個新的對象,不一樣的同步鎖對不一樣的線程不具備排他性,不能實現線程同步的效果,這時候線程同步就失效了。因此線程同步的一個前提:線程同步鎖對多個線程必須是互斥的,即多個線程須要使用同一個同步鎖。第一段代碼中obj對象被多個線程共享,可以實現同步。code
synchronized方法
除了synchronized代碼塊,synchronized關鍵字還能夠修飾方法,讓該方法進行線程同步,效果跟同步代碼塊同樣。
1 public synchronized void run() { 2 while (true) { 3 if (t > 0) { 4 try { 5 Thread.sleep(100); 6 System.out.println("當前線程:" + Thread.currentThread().getName() + "---" + t--); 7 } catch (InterruptedException e) {} 8 } 9 } 10 }
這時候synchronized後面沒有了expression,從哪兒獲取同步鎖呢?
也就是說使用同步方法的話,同步鎖只能是this或者當前類的字節碼對象。因此根據同步鎖必須互斥的前提,若是同時使用synchronized代碼塊和synchronized方法對同一個共享資源進行線程同步,synchronized代碼塊的同步鎖也必須跟synchronized方法同樣(要麼是this,要麼是類的字節碼對象)。
同步代碼塊和同步方法的區別
二者的區別主要體如今同步鎖上面。對於實例的同步方法,由於只能使用this來做爲同步鎖,若是一個類中須要使用到多個鎖,爲了不鎖的衝突,必然須要使用不一樣的對象,這時候同步方法不能知足需求,只能使用同步代碼塊(同步代碼塊能夠傳入任意對象);或者多個類中須要使用到同一個鎖,這時候多個類的實例this顯然是不一樣的,也只能使用同步代碼塊,傳入同一個對象。