1. 程式人生 > >物件及變數的併發訪問

物件及變數的併發訪問

掌握如下關鍵技術點:

  • synchronized物件監視器為Object時的使用
  • synchronized物件監視器為Class時的使用
  • 非執行緒安全是如何出現的
  • 關鍵字volatile的主要作用
  • 關鍵字volatile與synchronized的區別及使用情況

synchronized同步方法

  • 方法內的變數為執行緒安全
"非執行緒安全"問題存在於"例項變數"中,如果是方法內部的私有變數則不存在"非執行緒安全"問題(執行緒安全)
原因:方法內部是私有的特性
  • 例項變數非執行緒安全
如果多個執行緒共同訪問1個物件中的例項變數,則有可能出現"非執行緒安全"問題--可能出現覆蓋的情況
用執行緒訪問的物件中如果有多個例項變數,則執行的結果有可能出現交叉的情況
結論
:在兩個執行緒訪問同一個物件中的同步方法時一定是執行緒安全的.
  • 多個物件多個鎖
兩個執行緒分別訪問同一個類的兩個不同例項的相同名稱的同步方法,效果卻是以非同步的方式執行的.
原因:關鍵字synchronized取得的鎖都是物件鎖,而不是把一段程式碼或者方法(函式)當做鎖,哪個執行緒先執行帶synchronized關鍵字的方法,哪個執行緒就持有該方法所屬物件的鎖Lock,那麼其他執行緒只能呈等待狀態,前提是多個執行緒訪問的是同一個物件
但如果多個執行緒訪問多個物件,則JVM會建立多個鎖.
同步:synchronized 非同步asynchd
  • synchronized方法與鎖物件
呼叫關鍵字synchronized宣告的方法一定是排隊執行的,只有共享資源的讀寫訪問才需要同步化,如果不是共享資源,那麼根本沒有同步的必要

結論:

1.A執行緒先持有object物件的Lock鎖,B執行緒可以以非同步的方法呼叫object物件中的非synchronized型別的方法 2.A執行緒先持有object物件的Lock鎖,B執行緒如果在這是呼叫object物件中的synchronized型別的方法則需要等待,也就是同步

  • 髒讀
髒讀(dirtyRead):雖然賦值時進行了同步,但在取值時有可能出現一些意想不到的意外--在讀取例項變數時,此值已經被其他執行緒更改過了。
解決方法:新增synchronized關鍵字
  • synchronized鎖重入

在使用synchronized時,當一個執行緒得到一個物件鎖後,再次請求此物件鎖時是可以再次得到該物件的鎖的。

在一個synchronized方法/塊的內部呼叫本類的其他synchronized方法/塊時,是永遠可以得到鎖的。

"可重入鎖"的概念:自己可以再次獲得自己的內部鎖。比如有1條執行緒獲得了某個物件的鎖,此時這個物件鎖還沒有釋放,當其再次想要獲得這個物件的鎖的時候還是可以獲取的,如果不可重入鎖的話,就會造成死鎖。
當存在父子類繼承關係時,子類是完全可以通過"可重入鎖"呼叫父類的同步方法的
  • 出現異常,鎖自動釋放
當一個執行緒執行的程式碼出現異常時,其所持有的鎖會自動釋放
  • 同步不具有繼承性
同步不可以繼承

synchronized同步語句塊

synchronized方法是對當前物件進行加鎖,而synchronized程式碼塊是對某一個物件進行加鎖.
  • synchronized方法的弊端
用關鍵字synchronized宣告方法在某些情況下是有弊端的,比如A執行緒呼叫同步方法執行一個長時間的任務,那麼B執行緒則必須等待比較長的時間.在這樣的情況下可以使用synchronized同步程式碼塊來解決.
  • synchronized同步程式碼塊的使用
當兩個併發執行緒訪問同一個物件object中的synchronized(this)同步程式碼塊時,一段時間內只能有一個執行緒被執行,另一個執行緒必須等待當前執行緒執行完這個程式碼塊以後才能執行該程式碼塊.
  • 用同步程式碼塊解決同步方法的弊端
當一個執行緒訪問object的一個synchronized同步程式碼塊時,另一個執行緒仍然可以訪問該object物件中的非synchronized(this)同步程式碼塊。
優點:時間縮短,運算效率加快,synchronized同步程式碼塊做到真正的同步
  • 一半非同步,一半同步
不在synchronized程式碼塊中的程式碼是非同步執行,在synchronized塊中就是同步執行
  • synchronized程式碼塊間的同步性
在使用同步synchronized(this)程式碼塊時需要注意的是,當一個執行緒訪問object的一個synchronized(this)同步程式碼塊時,其他執行緒對同一個object中所有其他synchronized(this)同步程式碼塊的訪問將被阻塞,這說synchronized使用的"物件監視器"是一個.
  • 驗證同步synchronized(this)程式碼塊是鎖定當前物件的
和synchronized方法一樣,synchronized(this)程式碼塊也是鎖定當前物件的
  • 將任意物件作為物件監視器
多個執行緒呼叫同一個物件中的不同名稱的synchronized同步方法或者synchronized(this)同步程式碼塊時,呼叫的效果就是按順序執行,也就是同步的,阻塞的.
synchronized同步方法和synchronized(this)同步程式碼塊的作用:
1.對其他synchronized同步方法或者synchronized(this)同步程式碼塊呼叫呈阻塞狀態;
2.同一時間只有一個執行緒可以執行synchronized同步方法(同步程式碼塊)中的程式碼
synchronized(非this物件x)同步程式碼塊的作用:
1.在多個執行緒持有"物件監視器"為同一個物件的前提下,同一時間只有一個執行緒可以執行synchronized(非this物件x)同步程式碼塊中的程式碼
鎖非this物件具有一定的優點:如果在一個類中有很多個synchronized方法,這時雖然能實現同步,但會受到阻塞,所以影響執行效率;但是如果使用同步程式碼塊鎖非this物件,則非同步的,不與其他鎖this同步方法爭搶this鎖,則可以大大提高執行效率.
注意:"synchronized(非this物件x)同步程式碼塊"格式中物件監視器必須是同一個物件.如果不是同一個物件監視器則是非同步呼叫,出現交叉執行
多個執行緒呼叫一個方法是隨機的,可能出現髒讀。
  • 細化驗證3個結論
1.當多個執行緒同時執行synchronized(x){}同步程式碼塊時呈同步效果
2.當其他執行緒執行x物件中synchronized同步方法時呈同步效果
3.當其他執行緒執行x物件方法不加synchronized關鍵字的方法時也呈現同步效果
注意:如果其他執行緒呼叫不加synchronized關鍵字的方法時,還是非同步呼叫
  • 靜態同步synchronized方法與synchronized(class)程式碼塊
synchronized關鍵字加到static靜態方法上是給Class類上鎖,
而synchronized關鍵字加到非static靜態方法上是給物件上鎖
Class鎖可以對類的所有例項起作用
同步synchronized(class)程式碼塊的作用其實和synchronized static方法的作用是一樣的
  • 資料型別String的常量池特性
在JVM中具有String常量池快取的功能大多數情況下,同步synchronized程式碼塊都不使用String作為鎖物件,而改用其他的,比如new Object()例項化一個Object物件,但它並不放入快取中
  • 同步synchronized方法無限等待與解決
使用同步程式碼塊(非this物件x)的方式解決同步方法造成的死迴圈
  • 多執行緒死鎖
不同的執行緒都在等待根本不可能被釋放的鎖,從而導致所有的任務都無法繼續完成,死鎖會造成執行緒的"假死"只要出現相互等待對方釋放鎖就有可能出現死鎖
  • 內建類與靜態內建類
  • 內建類與同步:實驗1
在內建類中有兩個同步方法,但是用的卻是不同的鎖(物件監視器不同),列印的結果也是非同步的
  • 內建類與同步:實驗2
同步程式碼塊synchronized(class2)對class2上鎖後,其他執行緒只能以同步的方式呼叫class2中的靜態同步方法
  • 鎖物件的改變
在將任何資料型別作為同步鎖時,需要注意的是,是否有多個執行緒同時持有鎖物件,如果同時持有相同的鎖物件,則這些執行緒之間就是同步的;如果分別獲得鎖物件,這些執行緒之間就是非同步的.
提示:只要物件不變,即使物件的屬性被改變,執行的結果還是同步的

volatile關鍵字

關鍵字volatile的主要作用是使變數在多個執行緒間可見
  • 關鍵字volatile與死迴圈
  • 解決同步死迴圈
關鍵字volatile的作用是強制從公共堆疊中取得變數的值,而不是從執行緒私有資料棧中取得變數的值
  • 解決非同步死迴圈
私有堆疊的值與公共堆疊中的值不同步,解決這樣的問題需要使用volatile關鍵字,強制性從公共堆疊中進行取值.
關鍵字synchronized和volatile比較:
1.關鍵字volatile是執行緒同步的輕量級實現,所以volatile效能比synchronized好,並且volatile只能修飾於變數,而synchronized可以修飾方法以及程式碼塊.隨著JDK新版本的釋出,synchronized關鍵字在執行效率上得到了很大的提升,在開發中使用synchronized關鍵字的比率還是比較大的.
2.多執行緒訪問volatile不會發生阻塞,而synchronized會出現阻塞
3.volatile能保證資料的可見性,但不能保證原子性;而synchronized可以保證原子性,也可以間接保證可見性,因為它會將私有記憶體和公共記憶體中的資料做同步.
4.關鍵字volatile解決的是變數在多個執行緒之間的可見性;而synchronized關鍵字解決的是多個執行緒之間訪問的資源的同步性
執行緒安全包含原子性可見性兩個方面
  • volatile非原子的特性
關鍵字volatile雖然增加了例項變數在多個執行緒之間的可見性,但是它卻不具備同步性,那麼也就不具備原子性
關鍵字volatile主要使用的場合是在多個執行緒中可以感知例項變數被更改了,並且可以獲得最新的值使用,也就是用多執行緒讀取共享變數時可以獲得最新值使用
如果修改例項變數中的資料,比如i++,這樣的操作並不是一個原子操作,是非執行緒安全的.
i++操作步驟分解如下:
1.從記憶體中取出i的值;
2.計算i的值
3.將i的值寫到記憶體中
假如在第2步計算值的時候,另一個執行緒也修改i的值,那麼這個時候就會出現髒資料,解決辦法:使用synchronized關鍵字.
volatile本身並不處理資料的原子性,而是強制對資料的讀寫及時影響到主記憶體的
變數中記憶體中工作的過程:
1.read和load階段:從主記憶體複製變數到當前執行緒工作記憶體
2.use和assign階段:執行程式碼,改變共享變數的值
3.store和write階段:用工作記憶體資料重新整理主存對應變數的值
對於多個執行緒訪問同一個例項變數還是需要加鎖同步
  • 使用原子類進行i++操作
可以使用AtomicInteger原子類進行實現
原子操作是不能分割的整體,沒有其他執行緒能夠中斷或檢查正在原子操作中的變數,一個原子(atomic)型別就是一個原子操作可用的型別,它可以在沒有鎖的情況下做到執行緒安全(thread-safe)
  • 原子類也並不完全安全
原子類在具有邏輯性的情況下輸出的結果也是具有隨機性的
原子類的方法時原子的,但是方法與方法之間不是原子的,解決方法:使用同步。
  • synchronized程式碼塊有volatile同步的功能
關鍵字synchronized可以使多個執行緒訪問同一個資源具有同步性,而且它還具有將執行緒工作中的私有變數與公共記憶體中變數同步的功能。
特徵:互斥性可見性
總結:
有效控制執行緒間處理資料的順序性
對處理後的資料進行有效值的保證