Java 徹底弄明白synchronized的使用
阿新 • • 發佈:2019-01-26
多個執行緒訪問共享資源(臨界資源)的時候,會出現執行緒安全問題,安全問題大多數是可見性和原子性問題。但這樣說可能並不嚴謹,執行緒的安全性可能更在於他對錯誤性的定義,當多個執行緒訪問一個類時,如果可以需要考慮執行時環境的排程和交換,並且需要額外的同步保證結果正確,我們認為這個執行緒是有執行緒安全性問題的。下面我們討論一下可見性和原子性帶來的執行緒安全問題。
可見性的問題
例如執行多個執行緒執行a++,那麼多個執行緒就會被分配到不同的處理器上,每個處理器都從主存上覆制操作一份拷貝,處理完成後複製給主存。由於分配到了不同的處理器上,兩個執行緒的操作可能會互相覆蓋,這樣的結果就會和預想的又偏差。例如如下程式碼:
private volatile static int a=0; private static void add(){ a++; } public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub for(int i=0;i<1000;i++){ new Thread(new Runnable() { @Override public void run() { add(); } }).start(); } System.out.println(a); }
上面的例子中,我們期望是1000,然而他卻有時候輸出997,998的結果,這是因為執行緒A執行這個操作之後,並沒有返回過去,導致執行緒B訪問的時候,訪問到的不是最新的資料。當然上面也有原子性的問題,不過分開討論。
原子性分解的問題
原子性就是不可分割的操作,在硬體層面上是指不被執行緒排程器中斷,也就是說一個操作要麼執行完,要麼不執行。上面的例子中a++就不是原子性操作,首先要讀取X的值,然後+1,最後寫入工作記憶體中。三個步驟任何一部打斷,都會影響最終的結果。但是a=1和return就是原子性操作了。synchronized關鍵字
為了解決上述的原子性和可見性帶來的執行緒安全問題,Java提供了同步機制互斥鎖機制,這個機制保證了在同一時間內只有一個執行緒能訪問共享資源(臨界資源)。這個機制的保障來源於監視鎖Monitor,在Java中,每個物件都自帶監視鎖,當我們要訪問用synchronized修飾的方法或程式碼塊的時候,都意味著進入這個方法或者程式碼塊要加鎖,離開要放鎖。而且Synchronizd可以顯示的說明對哪個物件加鎖,如下例子:synchronized public void add(){ a++; } // 等價於 public void add(){ synchronized(this){ a++; } }
對同步機制互斥鎖小結
1、每個物件有自己的監視鎖Monitor,這意味著多個執行緒訪問一個物件的Synchronizd方法或者程式碼塊時,需要等其他執行緒放鎖才可訪問。相對的多個物件的監視鎖不存在互斥情況。 2、互斥機制只能保證Synchronizd程式碼塊裡的程式碼同步,而不再程式碼塊裡的程式碼不能得到保證。 3、子類重寫父類的Synchronizd方法不能保證同步。因為Synchronizd並不屬於方法定義的一部分。 4、SYnchronizd修飾靜態方法時,等同於給該類的所有物件都加了一把鎖。因為靜態方法屬於類,不屬於物件。 5,Synchronizd把類當監視鎖的話,效果等同4. 5、Synchronizd修飾的方法的監視鎖(Monitor)也是重入鎖,也就是說當一個執行緒獲得監視鎖之後,可以再次獲得該物件的鎖。例如,執行緒A呼叫一個物件的同步方法之後,可以呼叫該物件的另一個同步方法。參考資料:
Java併發開發實戰
蘭亭風雨:http://blog.csdn.net/ns_code/article/details/17199201
陽光日誌:http://blog.csdn.net/luoweifu/article/details/46613015
海子:http://www.cnblogs.com/dolphin0520/p/3923737.html