1. 程式人生 > >多執行緒學習筆記--02(物件及變數的併發訪問)

多執行緒學習筆記--02(物件及變數的併發訪問)

  1.學習目標    

     Synchronized物件監視器為Object時的使用

     Synchronized物件監視器為Class時的使用

     非執行緒安全是如何出現的,

     關鍵字volatile的主要作用

     關鍵字Synchronized和volatile的主要區別以及使用情況

   2. Synchronized物件監視器為Object時的使用

     1.synchronized可以修飾方法,兩個執行緒訪問同一個物件(這個物件只new了一次)中的同步方法的時候一定是執行緒安全的,即按照順序執行的,也就是說A執行緒執行完了再執行B執行緒。如果兩個執行緒訪問同一個類的不同的兩個例項(即便他的方法是同步的,這會建立兩個鎖),這其實資料不共享了,不會出現執行緒安全的問題,兩個執行緒是非同步執行的。為什麼他們是非同步執行?這就說明synchronized取得的鎖是物件鎖,而不是把一段程式碼或者方法當作鎖。需要注意的是:方法中的變數不存線上程安全的問題,永遠都是執行緒安全的,這是方法內部的私有變數的特性造成的。

    總結:

      A執行緒先持有object物件的Lock鎖(就是A執行緒呼叫object的同步方法),這時候B執行緒可以以非同步的方式呼叫object物件中的非synchronized型別的方法。

      如果A執行緒先持有object物件的Lock鎖(就是A執行緒呼叫object的同步方法),B執行緒這時候再呼叫object物件中的synchronized型別的方法時則需等待。

    髒讀:發生髒讀的情況是在讀取實力變數時,此值已經被其他執行緒修改過了。即便賦值的方法是同步的,取值的方法不同步也會出現髒讀的情況,解決辦法當然是同步。

    Synchronized鎖重入:自己可以再次獲取自己的內部鎖,比如有一條執行緒獲得了某個物件鎖,此時這個物件鎖還沒有被釋放,當其再次想要獲取這個物件的鎖的時候還是可以獲取的。比如在一個類中,一個執行緒同步的方法可以呼叫另一個執行緒同步的方法。如果不可重入鎖的話就會造成死鎖。需要注意的是:可重入鎖也支援在父子類繼承的環境中,當存在父子類繼承關係時,子類是完全可以通過“可重入鎖”呼叫父類的同步方法。需要注意的是:這裡是可重入鎖支援繼承,而不是同步支援繼承,鎖具有繼承性是指子類呼叫父類的同步方法,而同步不具有繼承性是子類重寫父類的同步方法不具有同步的效果。

    2.synchronized同步程式碼塊的使用,Synchronized方法存在的弊端,如果A執行緒需要執行一個長時間的任務,那麼B執行緒必須等待很長的時間,用同步程式碼塊可以解決這個問題。因為synchronized方法是對當前物件進行加鎖,而synchronized程式碼塊是對某一個物件(某一個物件可以是this,非this等等)進行加鎖。

     使用同步程式碼塊的好處當然是可以彌補同步方法的缺陷,即:使用同步程式碼塊來加快同步方法的執行時間,就是把可能出現執行緒不安全的賦值操作等等用synchronized(this)包裹起來。因為當一個執行緒訪問object的一個synchronized同步程式碼塊時。另一個執行緒仍然可以訪問該object物件中的非synchronized(this)同步程式碼塊。如果同步方法出現無限等待的問題,就是如果一個執行緒呼叫的同步方法陷入一個死迴圈,其他執行緒永遠沒有機會執行該物件的其他同步方法。有沒有解決的辦法呢?當然有,就是把同步方法換成同步程式碼塊代替,這樣進入方法的的時候就不會被阻塞。

    總結:

    同步程式碼塊和同步方法的作用:對其他synchronized方法或synchronized(this)同步程式碼塊呼叫呈阻塞狀態。同一時間只有一個執行緒可執行同步程式碼塊或同步方法。

   synchronized不僅可以鎖this物件,可以將任意物件作為物件監視器,這個任意物件大多數是例項變數及方法的引數,使用的格式為synchronized(非this物件),當然多個執行緒持有物件監視器持為同一個物件的前提下(必須是同一個鎖),同一時間只有一個執行緒可執行synchronized(非this物件)同步程式碼塊中的程式碼。

   鎖非this物件相比鎖this物件和同步方法的好處,如果在一個類中有很多個synchronized方法,這時雖然能夠實現同步,但是會受到阻塞,所以會影響執行效率,但是使用同步程式碼塊鎖非this物件。synchronized(非this)程式碼塊中的程式與同步方法是非同步的,不會與其他鎖this同步方法爭搶this鎖,則可大大提高執行效率。再次強調:物件監視器必須監視的是同一個物件(synchronized鎖的是同一個物件)如果監視的不是同一個物件,執行的結果就是非同步了。

   同步程式碼塊放在非同步的方法中進行宣告,並不能保證呼叫方法的執行緒的執行同步,也就是說執行緒呼叫方法的順序是無序的。雖然在同步塊中執行的順序是同步的,這樣極易出現髒讀的問題。

3.Synchronized物件監視器為Class時的使用

  synchronized作用在靜態方法上相當於對當前.java檔案所對應的Class類進行持鎖。而synchronized關鍵字加到非靜態方法是給物件上鎖。他們有什麼區別呢?由於類是由Class檔案生成,如果對Class檔案上鎖,那麼對類的所有例項起作用。

4.資料型別String的常量池特性

  因為JVM中具有String常量池的緩衝功能,所以String a=”a” String b=”a”這樣的話a和b是完全相等的。如果此時用synchronized鎖住這兩個相同的字串,當多個執行緒傳入相同的字串進去的時候,只會有一個執行緒被執行,因為鎖是一樣的,最好的是鎖一個new Object的物件,這樣拿到的是不同鎖,執行緒非同步執行。

 

最終總結:在將任何資料型別作為同步鎖時,需要注意的是,是否有多個執行緒同時持有鎖物件。如果同時持有相同的鎖物件,則這些執行緒之間就是同步的。如果分別獲得鎖物件,則這些執行緒之間就是非同步的。

5.關鍵字volatile的主要作用

   關鍵字volatile主要作用是使變數在多個執行緒之間可見,關鍵字volatile的作用是強制從公共堆疊中取得變數的值,而不是從執行緒私有資料棧中取得變數的值。

執行緒啟動的時候執行緒的變數會存在於公共堆疊及執行緒的私有堆疊中。在JVM被設定為-server模式時為了執行緒的效率,執行緒一直在私有堆疊中拿值,普通情況下執行緒修改變數的值只是修改公共堆疊的值,而私有堆疊的值仍未改變,所以解決這個問題的辦法就是用volatile修飾變數的值。迫使執行緒去從公共堆疊取值,達到變數在多個執行緒之間可見的一個效果。但是volatile最致命的缺點是不支援原子性。

  如何理解volatile不支援原子性,比如volatile修飾的變數i去執行++i,--i等運算的時候,當多個執行緒來執行這個操作的時候還是會出現不同步的情況,解決的辦法當然是使用synchronized來關鍵字。

  Synchronizedvolatile 的區別?

  1. 首先要明白的一點是volatile解決變數在多個執行緒之間的可見性。而synchronized是解決多個執行緒之間訪問資源的同步性。
  2. Volatile是執行緒同步的輕量級實現,效率要比synchronied要好,但是volatile只能修飾變數,而synchronized可以修飾方法和程式碼塊。
  3. 多執行緒訪問volatile不會發生阻塞,而synchronized會發生阻塞。
  4. Volatile能保證資料的可見性,但是不能保證資料的原子性。Sychronized既可以保證原子性也可以保證可見性,因為他會將私有記憶體和公共記憶體的資料做同步。

 Volatile的本質是JVM虛擬機器只是保證從主記憶體載入到執行緒工作記憶體的值是最新的。