synchronized鎖
synchronized鎖
對(duì)象鎖
- 一個(gè)對(duì)象一把鎖
類鎖
判斷是同步還是異步關(guān)鍵看是不是同一把鎖
- 鎖加在方法上或在方法中寫對(duì)象是個(gè)對(duì)象鎖
- 鎖加在類上或靜態(tài)方法上是類鎖
synchronized鎖的底層實(shí)現(xiàn)原理
jvm是基于進(jìn)入和退出Monitor對(duì)象來實(shí)現(xiàn)方法同步和代碼塊同步
代碼塊的同步
-
代碼塊的同步是利用monitorenter和monitorexit這兩個(gè)字節(jié)碼指令
- 分別位于同步代碼塊的開始和結(jié)束位置
當(dāng)jvm執(zhí)行到monitorenter指令的時(shí)候,試圖獲取monitor對(duì)象的所有權(quán),獲取成功鎖的計(jì)數(shù)器+1,當(dāng)執(zhí)行到monitorexit的時(shí)候,鎖計(jì)數(shù)器-1,當(dāng)鎖計(jì)數(shù)器=0,鎖就被釋放,如果獲取monitor對(duì)象失敗,就會(huì)進(jìn)入阻塞的狀態(tài)
方法級(jí)的同步為隱式
- 不是通過字節(jié)碼指令來控制,它實(shí)現(xiàn)在方法調(diào)用和返回之中
- JVM可以從方法常量池的方法表結(jié)構(gòu)(method_info Structure)的ACC_SYNCHRONIZED 訪問標(biāo)志區(qū)分一個(gè)方法是否同步方法
- 當(dāng)方法調(diào)用時(shí),調(diào)用指令檢查ACC_SYNCHRONIZED 訪問標(biāo)志是否被設(shè)置,如果設(shè)置了,該線程將先持有monitor,然后執(zhí)行方法,最終方法執(zhí)行完(無論成功、失?。┽尫舖onitor
synchronized鎖升級(jí)過程
1、無鎖狀態(tài)
2、偏向鎖
-
適合只有一個(gè)線程執(zhí)行同步代碼塊
- 當(dāng)有另外一個(gè)線程進(jìn)來立即轉(zhuǎn)成輕量級(jí)鎖
3、輕量級(jí)鎖樂觀鎖
- 多個(gè)線程競爭偏向鎖,偏向鎖關(guān)閉,升級(jí)成重量級(jí)鎖,中間過程有個(gè)鎖自旋(1.6引入自適應(yīng)鎖自旋,會(huì)根據(jù)鎖上一次的自旋時(shí)間來判斷自旋的次數(shù))
4、重量級(jí)鎖
synchronized鎖優(yōu)化
減少synchronized的范圍,同步代碼塊中的代碼盡量少
降低synchronized鎖的粒度
- 盡量不使用同一個(gè)鎖
分段鎖
- concurrentHashMap:局部鎖定
- hashtable:增刪該全部上鎖
讀寫分離,讀取時(shí)不加鎖,寫入和刪除的時(shí)候上鎖
鎖消除
-
去除那些不可能發(fā)生資源競爭的鎖
- 比如說一些線程安全的集合,默認(rèn)加了鎖,JVM根據(jù)逃逸分析進(jìn)行鎖消除
鎖粗化
-
將多個(gè)連續(xù)的加鎖、解鎖連在一起,擴(kuò)成一個(gè)更大范圍的鎖
- for循環(huán),加鎖放在循環(huán)外面
1.6引入的自適應(yīng)鎖自旋
- 根據(jù)鎖上一次的自旋時(shí)間來判斷
CAS
定義:在并發(fā)量不是很高的情況下,先查一次,然后進(jìn)行修改,在真正的寫數(shù)據(jù)時(shí),會(huì)再查一次,比較兩次查詢的結(jié)果,如果不一樣說明是不安全的,如果一樣說明安全,會(huì)進(jìn)行修改
在高并發(fā)的情況下,會(huì)有一個(gè)忙循環(huán)的過程,消耗cpu
ABA問題
- 通過維護(hù)一個(gè)版本號(hào)來解決
- 之前讀和過段時(shí)間讀,可能中間會(huì)被第三個(gè)人修改過,但是又被改回來
CAS只能針對(duì)一個(gè)共享變量
ReentrantLock與synchronized的區(qū)別
1、從jvm角度來看synchronized其實(shí)jvm的一個(gè)關(guān)鍵字,ReentrantLock是一個(gè)類
2、synchronized不需要關(guān)心鎖的釋放,ReentrantLock需要手動(dòng)lock利用try..catch..finanlly釋放鎖
所有線程能夠看到共享內(nèi)存的最新狀態(tài)
volatile關(guān)鍵字
保證內(nèi)存的可見性
防止指令重排
- 防止Jvm繼續(xù)指令重拍優(yōu)化
- PS:單線程下指令重排會(huì)考慮數(shù)據(jù)的依賴性,不會(huì)影響執(zhí)行結(jié)果
2、多線程會(huì)有影響
不保證原子性
-
使用并發(fā)包下的Atomic的類
- 如AtomicInteger.....
PS:底層通過屏障指令保證可見性、有序性,刷新主從的數(shù)據(jù)
ps:鎖是不可逆的,一旦升級(jí)成重量級(jí)的鎖就不變了
lock與synchronized的區(qū)別
lock需要手動(dòng)加鎖、釋放鎖
synchronized在發(fā)生異常,會(huì)自動(dòng)釋放線程占有鎖,不會(huì)發(fā)生死鎖,lock必須將unLock()放到finally{} 中
lock支持實(shí)現(xiàn)公平鎖(按照加鎖的順序,先來的先拿到鎖)和非公平鎖,synchronized只支持非公平鎖
死鎖產(chǎn)生的四個(gè)必要條件
互斥條件
- 在某段時(shí)間某個(gè)資源被一個(gè)線程占用,另外一個(gè)線程請(qǐng)求資源,只能等待
請(qǐng)求和保持
- 進(jìn)程已至少保持一個(gè)資源,又提出了新的資源請(qǐng)求,而其他資源又被占用,此時(shí)請(qǐng)求線程阻塞,自己已獲取的資源又不能釋放
不剝奪條件
- 自己已獲取的資源,在未使用完之前,不能被剝奪,只有使用完才可釋放
環(huán)路等待條件
- 發(fā)生死鎖時(shí),存在進(jìn)程---資源的環(huán)形鏈
案例
- Test01.java
synchronized保證內(nèi)存可見性

image.png
AQS
定義:隊(duì)列同步器
兩種資源共享方式
-
Exclusive
- 獨(dú)占,只有一個(gè)線程能執(zhí)行,如ReentrantLock
-
Share
- 共享,多個(gè)線程可同時(shí)執(zhí)行,如Semaphore/CountDownLatch
本質(zhì)使用CLH的同步隊(duì)列存放線程資源
-
先進(jìn)先出
入列
出列
鎖的基本概念
互斥鎖
- 每個(gè)對(duì)象都對(duì)應(yīng)一個(gè)可稱為“互斥鎖”的標(biāo)記,保證在任意時(shí)刻,只有一個(gè)線程可以訪問該資源
阻塞鎖
- 讓線程進(jìn)入阻塞狀態(tài)
自旋鎖
讀寫鎖
公平鎖
- 排隊(duì),先來先得
非公平鎖
- 不考慮排隊(duì)問題
ReentrantLock
相比于synchronized性能會(huì)好些
互斥鎖
- 同一時(shí)間僅有一個(gè)線程可以訪問,所以就有了ReentrantReadWriteLock讀寫鎖
ReentrantReadWriteLock
- 讀寫鎖ReentrantReadWriteLock實(shí)現(xiàn)接口ReadWriteLock,該接口維護(hù)了一對(duì)相關(guān)的鎖,一個(gè)用于只
讀操作,另一個(gè)用于寫入操作。只要沒有 writer,讀取鎖可以由多個(gè) reader 線程同時(shí)保持。寫入鎖是
獨(dú)占的
鎖降級(jí)
- 獲取寫鎖-》獲取讀鎖-》釋放寫鎖
JUC
CyclicBarrier
- 同步屏障
Semaphore
- 通過已有的信號(hào)量,對(duì)資源的線程訪問數(shù)量進(jìn)行控制,有公平和非公平兩種實(shí)現(xiàn)方式
CountDownLatch
- 計(jì)數(shù)器沒到,繼續(xù)等待,到了0就可以執(zhí)行了
- PS:計(jì)數(shù)無法被重置