網站首頁 編程語言 正文
目錄
1.java為什么要實現自己的管程機制?
2.AQS
?
2.1 state
?2.2 同步等待隊列和條件等待隊列
?2.3 自定義AQS
3.reentrantLock詳解
3.1 源碼分析(TODO)
1.java為什么要實現自己的管程機制?
管程:操作系統底層實現的對線程并發問題實現的監視器機制,管程內部大致分為:同步等待隊列和等待喚醒隊列。
同步等待隊列的實現:synchronized在jvm底層保證線程能夠串行化的操作同步代碼塊內的程序。
等待喚醒隊列的實現:java在object類中實現了monitor機制,每個對象都有一個monitor,可以手動調用wait/notify/notifyall方法操作線程。
synchronized缺點:不能手動加鎖解鎖,synchronized內部加解鎖是自動的。
為了解決以上場景java實現了自己的管程機制AQS機制:
同步等待隊列(雙向鏈表結構):cas實現(內部通過volatile int state狀態控制入隊出隊) 加解鎖有自己的實現
條件等待隊列(單向鏈表結構):Condition接口實現(await/signal/signalAll等待;喚醒機制)
2.AQS
基于上述描述講解AQS。
AQS的特性:
? ? ? ? 1. 阻塞等待隊列
? ? ? ? 2. 共享/獨占
? ? ? ? 3. 公平/非公平
? ? ? ? 4. 可重入
? ? ? ? 5. 允許中斷
AQS的實現類如下圖。
AQS的資源共享方式
????????獨占:只有一個線程能執行,如reentrantLock
????????共享:多個線程同時執行,如Semaphore和CountDownLatch
AQS的兩種隊列:同步等待隊列和條件等待隊列
????????同步等待隊列:主要用于維護獲取鎖失敗的線程
????????條件等待隊列:調用await時會釋放鎖進入條件等待隊列,調用signal時喚醒線程放入同步等待隊列
AQS中實現的方法:
????????共享:tryAcquireShared獲取共享鎖,releaseShared解鎖,tryReleaseShared嘗試解鎖
????????獨占:tryAcquire獲取獨占鎖,release解鎖,tryRelease嘗試解鎖
2.1 state
AQS中volatile int修飾的state:表示AQS的狀態
state的訪問方式:
getState(),setState(),compareAndSetState()
?
?2.2 同步等待隊列和條件等待隊列
同步等待隊列是一個雙向鏈表的隊列。
條件等待隊列是一個單向鏈表結構,條件調用await喚醒以后進入同步等待隊列,同步等待隊列阻塞進入條件等待隊列。
?2.3 自定義AQS
自定義自己的AQS實現類:
1.繼承AbstractQueuedSynchronizer
2.實現自己的抽象方法tryAcquire和tryRelease方法。其他方法都是AQS已經封裝好的直接調用即可。
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class TestLock extends AbstractQueuedSynchronizer {
private int state_0 = 0;//0-解鎖
private int state_1 = 1;//1-加鎖
//加鎖邏輯:使用CAS實現,如果修改成功返回true并且設置獨占鎖,否則返回false
@Override
protected boolean tryAcquire(int arg) {
if(compareAndSetState(state_0,arg)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//釋放鎖:將鎖狀態還原
@Override
protected boolean tryRelease(int arg) {
setExclusiveOwnerThread(null);
setState(arg);
return true;
}
public void lock(){
acquire(state_1);
}
public boolean tryLock(){
return tryAcquire(state_1);
}
public void unLock(){
release(state_0);
}
public boolean tryUnLock(){
return tryRelease(state_0);
}
}
class TestAQS{
private static int sum = 0;
private static TestLock testLock = new TestLock();
public static void main(String[] args) throws InterruptedException {
for (int i =0 ;i<3;i++){
Thread thread = new Thread(()->{
testLock.lock();
try{
for (int j=0;j<10000;j++){
sum++;
}
}finally {
testLock.unLock();
}
});
thread.start();
}
//休眠1秒等待線程執行完畢
Thread.sleep(1000);
System.out.println("sum:"+sum);
}
}
3.reentrantLock詳解
reentrantLock是一個基于AQS框架的鎖,可以手動加解鎖,比synchronized性能要好一點,支持解決并發安全問題,支持公平和非公平鎖。
synchronized與reenTrantLock的區別:最重要的區別就是reenTrantLock需要在finally塊中手動解鎖。
reenTrantLock的特點:
? ? ? ? 1.可中斷
? ? ? ? 2.可設置超時時間
? ? ? ? 3. 可以設置公平鎖
? ? ? ? 4. 支持多個條件變量
? ? ? ? 5. 支持可重入
3.1 源碼分析(TODO)
關注點:
1.加解鎖
2.公平,非公平,可重入實現
3.線程競爭鎖失敗入隊阻塞邏輯和獲取鎖的線程釋放鎖喚醒阻塞線程競爭鎖的邏輯實現
重點debug下面代碼:lock.lock和lock.unlock源碼部分
1.lock.lock(加鎖,默認非公平鎖實現):CAS判斷,如果修改成功,設置獨占鎖,沒有走公平鎖邏輯獲取鎖。
? ? ? ? 1.1 公平鎖邏輯分為幾步:
? ? ? ? ? ? ? ? 1.1.1 嘗試獲取鎖(tryAcquire(int acquires)),分為兩步:1.線程狀態state為0執行CAS獨占鎖邏輯。2.如果當前線程是獨占鎖執行重入鎖邏輯,將state加1。
? ? ? ? ? ? ? ? 1.1.2 入隊(addWaiter(Node.EXCLUSIVE)),桟入隊操作有點兒復雜(TODO),簡單來說就是雙向鏈表的建立過程。
? ? ? ? ? ? ? ? 1.1.3 獲取隊列(boolean acquireQueued(final Node node, int arg)),也是分為兩步:1.如果是只有一個節點并且獲取鎖成功返回中斷標志位為false;2.如果不是只有一個節點通過waitstatus標志位(-1 喚醒鎖,大于0)回去嘗試喚醒鎖或者(TODO,看不懂了,感覺有兩個隊列)
? ? ? ? 1.2 設置中斷標志位(selfInterrupt()):前面的操作會取消中斷標志位,這一步是為了復位中斷標志位
2.lock.unlock(解鎖):解鎖邏輯很簡單,分為兩步
???2.1 嘗試將重入標志state值減1判斷是否等于0,tryRelease(arg),如果為0,復原獨占鎖為null? ? ? ? 2.2? 上一步的state為0時并且鏈表不為空,喚醒標志位waitstatus不為0,阻塞線程
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private static int sum = 0;
private static Lock lock = new ReentrantLock();
//private static TulingLock lock = new TulingLock();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(()->{
//加鎖
lock.lock();
try {
// 臨界區代碼
// TODO 業務邏輯:讀寫操作不能保證線程安全
for (int j = 0; j < 10000; j++) {
sum++;
}
} finally {
// 解鎖
lock.unlock();
}
});
thread.start();
}
Thread.sleep(2000);
System.out.println(sum);
}
}
原文鏈接:https://blog.csdn.net/qq_21575929/article/details/124581277
相關推薦
- 2022-03-10 通過容器擴展屬性IExtenderProvider實現WinForm通用數據驗證組件_C#教程
- 2022-12-03 C#通過Builder模式造車_C#教程
- 2022-06-29 徹底掌握C語言strcat函數的用法_C 語言
- 2022-09-09 ElasticSearch核心概念_其它綜合
- 2023-04-11 Golang使用協程實現批量獲取數據_Golang
- 2023-12-09 使用String.valueOf()的坑
- 2023-04-08 c#?線程定時器?System.Threading.Timer的使用_C#教程
- 2022-10-11 sharedUid、系統簽名、預置應用
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支