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來加鎖和解鎖,保證了同時只有一個線程可以執行指定代碼,從而保證了線程安全,同時具有可重入性和不可中斷的性質。