一、Java併發共享受限資源的問題
在使用Java併發時,如果兩個或者多個線程需要訪問共享資源,那麼就會出現共享資源競爭的問題,多個線程試圖同時訪問同一個共享資源。
當線程A與線程B都是對共享資源進行寫入數據,那麼由於線程調度機制的不確定性,在線程A寫入一半數據時可能出現線程B調度,並寫入數據,這將導致數據錯誤。
二、解決共享資源競爭
使用線程時的一個基本問題是:你永遠不知道一個線程何時在運行。因此對於併發工作,需要某種方式來防止兩個任務訪問相同的資源,至少在關鍵階段不能出現競爭的狀況。
防止衝突的方法就是當資源被一個任務使用時,在其上加鎖。第一個訪問某項資源的任務必須鎖定這項資源,使其他任務在其被解鎖之前無法訪問它,而在其被解鎖之時,另一個任務就可以鎖定並使用它,以此類推。
基本上所有的併發模式在解決線程衝突問題的時候,都是採用序列化訪問共享資源的方案,這意味着在給定時刻只允許一個任務訪問共享資源。
三、原子性、可見性與有序性
內存模型的三大特性爲:原子性、可見性與有序性。
四、臨界區
有時候希望防止多個線程同時訪問方法內部的部分代碼而不是防止訪問整個方法,那麼可以通過synchronized對代碼進行分離,該代碼段稱爲臨界區。synchronized被用來指定某個對象,此對象的鎖被用來對花括號內的代碼進行同步控制。
這也被稱爲同步控制塊,在進入此段代碼之前,必須得到syncObjcet對象的鎖,如果其他線程已經得到這個鎖,那麼就等到鎖被釋放後才能進入臨界區。
通過使用同步控制塊,而不是對整個方法進行同步控制,可以使多個任務訪問對象的時間性能得到顯著提高。
五、在其他對象上同步
前面提到的在方法前面使用synchronized關鍵字進行修飾,事實上是對方法對應類進行上鎖,等同於使用synchronized(this)對類對象進行上鎖。
上述代碼中,方法f()和time()中的鎖的對象是不一致的,f()方法的鎖對象是類對象本身,而time()方法鎖對象是syncObject,因此可以在不同線程中對同一對象進行同步。
參考資料:《Java編程思想》
內存可見性和原子性:synchronized和volatile的比較
Java併發編程(三)volatile域