java併發編程synchronize關鍵字

1.Synchronized的作用

能夠保證在同一時刻最多隻有一個線程執行該段代碼,以達到保證併發安全的效果。

2.Synchronized的地位

1.Synchronized是Java的關鍵字,被Java語言原生支持

2.是最基本的互斥同步手段

3.是併發編程中的元老級角色,是併發編程的必學內容

3.Synchronized的兩個性質

1.可重入(synchronized區別於其他鎖的一個很重要的性質)

什麼是可重入:指的是同一線程的外層函數獲得鎖之後,內層函數可以直接再次獲取該鎖。也叫做遞歸鎖。Java中兩大遞歸鎖:Synchronized和ReentrantLock

好比買車搖號,只要搖到一次號,可以給家裏的第一輛車、第二輛車...所有車都上牌,直到我不需要上牌爲止。這就叫做可重入性。如果每輛車上牌都需要搖一次號,這就叫做不可重入性。

鎖的不可重入性:線程拿到一把鎖了,如果想再次使用這把鎖,必須先將鎖釋放,與其他線程再次進行競爭。
鎖的可重入性:如果線程已經拿到了鎖,試圖去請求這個已經獲得到的鎖,而無需提前釋放,直接就可以使用手裏的這把鎖,這就叫做可重入性

好處:避免死鎖、提升封裝性

1.假如有兩個synchronized修飾的方法1和方法2,此時線程A執行到了方法1,並且獲得了方法1的鎖,此時方法1調用了方法2,由於方法2也是synchronized修飾的,假設synchronized不具備可重入性的話,那麼線程A雖然拿到了方法1的鎖,但是由於不可重入,它無法使用本身獲得的方法1的這把鎖。這樣一來,它既想拿鎖又不釋放鎖,這樣就會永遠等待,形成了死鎖。所以由於synchronized具備可重入性,就避免了這種情況的發生。

2.避免了一次又一次的解鎖加鎖的過程,利用其可重入的性質提高了封裝性,簡化了併發編程的難度。

粒度:線程而非調用(用三種情況來說明和pthread的區別)

情況1:證明同一個方法是可重入的

情況2:證明可重入不要求是同一個方法

情況3:證明可重入不要求是同一個類中的

2.不可中斷(相比於其他有的鎖可以中斷,這個性質是synchronized的一個劣勢所在)

一旦這個鎖已經被別的線程獲得了,如果當前線程還想獲得,只能選擇等待或者阻塞,直到別的線程釋放這個鎖。如果別的線程 永遠不釋放鎖,那麼線程只能永遠地等下去。

相比之下,Lock類,可以擁有中斷的能力。

第一點,如果我覺得我等的時候太長了,有權中斷現在已經獲取到鎖的線程的執行; 第二點,如果我覺得我等待的時間太長了不想再等了,也可以退出。

Lock比synchronized靈活很多,但是編碼易出錯。

4.Synchronized的兩個用法

對象鎖

包括方法鎖(默認鎖對象爲this當前實例對象)和同步代碼塊鎖(自己指定鎖對象)

方法鎖形式:synchronized修飾普通方法,鎖對象默認爲this

代碼塊形式:手動指定鎖對象

類鎖

指synchronized修飾靜態的方法或指定鎖爲Class對象

概念(重要):

只有一個Class對象:Java類可能有很多個對象,但只有一個Class對象

本質:所謂的類鎖,不過是Class對象的鎖而已

用法和效果:類鎖只能在同一時刻被一個對象擁有。

形式一:synchronized加在static方法上

形式二:synchronized(*.class)代碼塊

 

多線程訪問同步方法的七種情況(面試常考)

1.兩個線程同時訪問一個對象的同步方法(對象鎖)

這種情況就是對象鎖的方法鎖情況。會相互等待,只能有一個線程持有鎖。

2.兩個線程訪問的是兩個對象的同步方法

不會加鎖,因爲訪問的是不同的實例

3.兩個線程訪問的是synchronized的靜態方法

這種情況就是類鎖的靜態方法鎖。

4.同時訪問同步方法與非同步方法

synchronized關鍵字只作用於當前方法,不會影響其他未加關鍵字的方法的併發行爲。因此非同步方法不受到影響,還是會併發執行。

5.訪問同一個對象的不同的普通同步方法(對象鎖)

synchronized關鍵字雖然沒有指定所要的那個鎖對象,但是本質上是指定了this這個對象作爲它的鎖。所以對於同一個實例來講,兩個方法拿到的是同一把鎖,因此會出現串行的情況。

6.同時訪問靜態synchronized和非靜態synchronized方法

前者爲類鎖,鎖爲Class類;後者爲對象鎖,鎖爲this對象。因此兩者的鎖不同,會並行執行。

7.方法拋異常後,會釋放鎖麼?

Lock類加鎖時,如果出現異常,不顯式手動釋放鎖的話,Lock是不會釋放的。

而synchronized不同,一旦出現異常,會自動釋放鎖。

也就是說當第二個線程等待一個被synchronized修飾的方法時,若第一個線程出現異常退出,這把鎖會立刻釋放並且被第二個線程所獲取到。JVM會自動把鎖釋放。

8.擴展:線程進入到一個被synchronized修飾的方法,而在這個方法裏面調用了另外一個沒有被synchronized修飾的方法,這個時候還是線程安全的嗎?

七種情況總結:三點核心思想

1.一把鎖只能同時被一個線程獲取,沒有拿到鎖的線程必須等待(對應第1、5種情況);2.每個實例都對應有自己的一把鎖,不同實例之間互不影響;
  例外:鎖對象是*。class以及synchronized修飾的是static方法的時候,所有對象共用同一把類鎖,這就是類鎖的兩種情況(對應第2、3、4、5種情況);3.無論方法是正常執行完畢或者拋出異常,都會釋放鎖(對應第7種情況)。

Synchronized的缺陷

1.效率低:

  • 鎖的釋放情況少

  • 試圖獲得鎖時不能設定超時

  • 不能中斷一個正在試圖獲得鎖的線程

2.不夠靈活(讀寫鎖更靈活:讀操作的時候不會加鎖,寫操作的時候纔會加鎖):

  • 加鎖和釋放的時機單一

  • 每個鎖僅有單一的條件(某個對象),可能是不夠的

3.無法知道是否成功獲取到鎖

Lock可以,如果嘗試成功了做一些邏輯判斷,如果沒有成功做另外一些邏輯判斷.

Lock類:

lock.lock();lock.unlock();

通過這兩個方法,可以手動加鎖和解鎖。

lock.tryLock();lock.tryLock(10, TimeUnit.MINUTES);

可以判斷是否加鎖,返回類型爲boolean

答案:不是的。出了本方法後,由於另外的方法沒有被synchronized修飾,所以說這個方法可以被多個線程同時訪問的。

 

總結

一句話介紹synchronized:

JVM會自動通過使用monitor來加鎖和解鎖,保證了同時只有一個線程可以執行指定代碼,從而保證了線程安全,同時具有可重入性和不可中斷的性質。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章