可重入鎖,不可重入鎖,死鎖的多種情況,以及產生的原因,如何解決,synchronized采用的鎖策略(渣女圣經)自適應的底層,鎖清除,鎖粗化,CAS的部分應用

?一、💛

鎖策略——接上一篇

6.分為可重入鎖,不可重入鎖

如果一個線程,針對一把鎖,連續加鎖兩次,會出現死鎖,就是不可重入鎖,不會出現死鎖,就是可重入鎖。

如果一個線程,針對一把鎖,連續加鎖兩次,如果產生了死鎖,就是不可重入鎖😄

public class Demo5 {public static void main(String[] args) {Thread t=new Thread(new Runnable() {int count=0;@Overridepublic synchronized void run() {    //加了一層鎖synchronized (this){            //加了第二次鎖count++;}System.out.println(count);}});t.start();}
}

那么我們來解釋一下什么叫做死鎖呢?

public synchronized void run() { ? ?//加了一層鎖
? ? ? ? ? ? ? ? synchronized (this){ ? ? ? ? ? ?//加了第二次鎖
? ? ? ? ? ? ? ? ? ? count++;
? ? ? ? ? ? ? ? }

這個代碼中,調用方法先針對this加鎖,此時假設加鎖成功了,接下來到往下執行代碼塊中的this來進行加鎖,此時就會出現鎖競爭,this已經處于鎖狀態了,此時該線程就會阻塞~一直阻塞到鎖被釋放,才能有機會拿到鎖。

這也是死鎖第一個體現:this這個鎖必須要run執行完畢,才能釋放,但是要想執行完事,這個第二次加鎖就應該加上,方法才可以執行,但是第二次想加上第一個就應該放鎖,所以由于this鎖沒法釋放,代碼就卡在這里了,因此線程數量就僵住了。

還好synchronized是可重入鎖,JVM幫我們承擔了很多的任務

這里卡死就很不科學的一種情況,第二次嘗試加鎖的時候,該線程已經有了這個鎖的權限了~~這個時候,不應該加鎖失敗,不應該進行阻塞等待的~

不可重入鎖:這把鎖不會保存,哪個線程加上的鎖,只要他當前處于加鎖狀態之后,收到了‘加鎖的請求’,就會拒絕當前加鎖,而不管當下線程是哪個,就會產生死鎖。?🌝

可重入鎖:會讓這個鎖保存,是哪個線程加的鎖,后續收到加鎖請求之后,就會先對比一下,看看加鎖的線程是不是當前持有自己這把鎖的線程~~這個時候就可以靈活判定了。

那么該如何對比捏🌚

synchronized(this){synchronized(this){synchronized(this){
······->執行到這個代碼,出了這個代碼,剛才加上的鎖,是否要釋放?}       如果最里面的釋放了鎖,意味著最外面的synchronized和中間的synchronized后
}           續的代碼部分就沒有處在鎖的保護之中了

真正要在這個地方釋放鎖,如加鎖N層遇到了 } , JVM如何知道是最后一個呢,整一個整型變量,記錄當前這個線程加了幾次鎖,每遇到一個加鎖操作,計數器+1,每遇到一個解鎖操作,就-1,當計數器減為0時,才真正執行釋放鎖操作,其他時候時不釋放的。這一個思想就叫做‘引用計數’🐲🐲🐲(腦力+10000,人類進化不帶我)

注補充:靜態方法是針對類加鎖,普通方法是針對this加鎖


二、💙

死鎖的詳細介紹:兩次加鎖,都是同一個線程

死鎖的三種典型情況;

1.一個線程,一把鎖,但是不可入鎖,該線程針對這個鎖聯系加兩次就會出現死鎖。

2.兩個鎖,兩個鎖,這兩個縣層先分別獲取一把鎖,然后再嘗試分別獲取對方的鎖(

比如我拿了醬油要炫餃子,小楊拿醋,我讓他把醋先給我,然后我一起給你,小楊一拍桌子,憑啥先給你,你多個啥?),如下圖,雙方陷入死循環中

?

public class Demo5 {public static Object  locker1=new Object();public static Object  locker2=new Object();public static void main(String[] args) {Thread t1=new Thread(new Runnable() {@Overridepublic synchronized void run() {synchronized (locker1) {           //給1加鎖System.out.println("s1.start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker2) {    //沒有放棄1的鎖System.out.println("s2.over");}}}});t1.start();Thread t2=new Thread(new Runnable() {@Overridepublic synchronized void run() {synchronized (locker2) {         //給2加鎖System.out.println("t2.start");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker1) {     //沒有放棄2的鎖System.out.println("t1.over");}}}});t2.start();}
}

3.N個線程,M把鎖:

(哲學家就餐問題)


?三、💜

如何應該避免死鎖呢?先明確死鎖產生的原因,死鎖的必要條件(缺一不可)

1.互斥使用:一個線程獲取到一把鎖之后,別的線程不能獲取到這個鎖。(實際使用的鎖,一般都是互斥的鎖的基本特性)

2.不可搶占:鎖只能被持有者主動釋放,而不能是其他線程直接搶走(也是鎖的基本特性)

3.請求和保持:這一個線程嘗試獲取多把鎖,在獲取第二把時候,會保持對第一把的獲取狀態(取決于代碼結構)比如剛才寫的,我只要讓他獲取完第一把再釋放,在獲取第二把,這樣不發生沖突,但是可能會影響需求。

4.循環等待:t1嘗試獲取locker2,需要t2執行完釋放locker2,t2嘗試獲取locker1,需要t1執行完畢,釋放locker1(取決于代碼結構)

我們的解決方式趨向于解決第四種(打破循環等待,如何具體實現解決死鎖,實際方法有很多)

首先就是銀行家算法(殺牛刀了屬于,復雜,沒必要會)

簡單有效方法:針對鎖進行編號,且規定加鎖的順序,只要線程加鎖的順訊,都嚴格執行上述順序,就沒有循環等待。

如下:

一般面試我們主動點: ?問到死鎖撿著了,細細的答,給他講,讓他覺得你是理解的

1.什么是死鎖。 ? ? ? ? ??

2.死鎖的幾個典型場景

3.死鎖產生的必要條件

4.如何解決死鎖的問題

?


四、???

synchronized具體采用了哪些鎖策略呢?

1.既是悲觀鎖,又是樂觀鎖

2.既是重量級鎖,又是輕量級鎖

3.重量級鎖部分是基于多系統互斥鎖實現的,輕量級鎖部分是基于自旋鎖實現的

4.synchronized是非公平鎖(不會遵守先來后到,鎖釋放之后,哪個線程拿到鎖個憑本事

5.synchronized是可重入鎖(內部會記錄哪個線程拿到了鎖,記錄引用計數)

6.synchronized不是讀寫鎖

synchronized-內部實現策略(自適應)

講解一下自適應:代碼中寫了一個synchhronized之后,可能產生一系列自適應的過程,鎖升級(鎖膨脹)

無鎖->偏向鎖->輕量級鎖->重量級鎖

偏向鎖,不是真的加鎖,而只是做了一個標記,如果有別的線程來競爭鎖,才會真的加鎖,如果沒有別的線程競爭,就自始至終都不加鎖了(渣女心態,沒人來追你,我就釣魚,你要是被追了,我先給你個身份,讓別人別靠近你。)——當然加鎖本身也有一定消耗

偏向鎖在沒人競爭的時候就是一個簡單的(輕量的)標記,如果有別的線程來嘗試加鎖,就立即把偏向鎖升級成真正加鎖,讓別人阻塞等待(能不加鎖就不加鎖)

輕量級鎖-synchronized通過自旋鎖的方式實現輕量級鎖——這邊把鎖占據了,另一個線程按照自旋的方式(這個鎖操作比較耗cpu,如果能夠快速拿到鎖,多耗點也不虧),來反復查詢當前的鎖狀態是不是被釋放,但是后續,如果競爭這把鎖的線程越來越多了(鎖沖突更加激烈了),從輕量鎖,升級到重量級鎖~隨著競爭激烈,即使前一個線程釋放鎖,也不一定能夠拿到鎖,何時能拿到,時間可能比較久了會

?💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖💖

鎖清除:編譯器,會智能的判斷,當前這個代碼,是否有必要加鎖,如果你寫了加鎖,但實際沒必要加鎖,就會自動清除鎖

如:單個線程使用StringBuffer編譯器進行優化,是保證優化之后的邏輯和之前的邏輯是一致的,這樣就會讓代碼優化變的保守起來~~咱們猿們也不能指望編譯器優化,來提升代碼效率,自己也要有作用,判斷何時加鎖,也是咱們非常重要的工作。

鎖粗化:

關于鎖的粒度,鎖中操作包含代碼多:鎖粒就大

//1號       全寫的是偽代碼
和2號比較明顯是2號的粒度更大
for(
synchronized(this){count++}
}//2號
synchronized(this){
for{
count++}
}

鎖粒大,鎖粒小各有好處:

鎖粒小,并發程度會更高,效率也會更快

鎖粒大,是因為加鎖本身就有開銷。(如同打電話,打一次就行,老打電話也不好)

上述的都是基本面試題


五、💚

CAS全稱(Compare and swap) 字面意思:比較并且交換

能夠比較和交換,某個寄存器中的值和內存中的值,看是否相等,如果相等就把另一個寄存器中的值和內存進行交換

boolean CAS(address,expectValue,swapValue){if(&address==expectValue){         //這個&相當于C語言中的*,看他兩個是否相等&address=swapValue;             //相等就換值return true;                 
}return false;

此處嚴格的說是,adress內存的值和swapValue寄存器里的值,進行交換,但是一般我們重點關注的是內存中的值,寄存器往往作為保存臨時數據的方式,這里的值是啥,很多時候我們選擇是忽略的。

這一段邏輯是通過一條cpu指令完成的(原子的,或者說確保原子性)給我們編寫線程安全代碼,打開了新的世界。

CAS的使用

1.實現原子類:多線程針對一個count++,在java庫中,已經提供了一組原子類

java.util.concurrent(并發的意思).atomic

AtomicInteger,AtomicLong,提供了自增/自減/自增任意值,自減任意值··,這些操作可以基于CAS按照無鎖編程的方式來實現。

如:for(int i=0;i<5000;i++){

count.getAndIncrement(); ? ? ? ? ? ? ? ? ? ? ? ? //count++

count.incrementAndGet(); ? ? ? ? ? ? ? ? ? ? ? ?//++count

count.getAndDecrement(); ? ? ? ? ? ? ? ? ? ? ?//count--

count.decrementAndGet() ? ? ? ? ? ? ? ? ? ? ?//--count

}

import java.util.concurrent.atomic.AtomicInteger;public class Demo6 {public  static AtomicInteger count=new AtomicInteger(0);    //這個類的初值唄public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{for (int i=0;i<500;i++){count.getAndIncrement();}});Thread t2=new Thread(()->{for (int i=0;i<500;i++){count.getAndIncrement();}});t1.start();t2.start();t1.join();            //注意要等待兩個線程都結束再開始調用t2.join();System.out.println(count);}
}

上述原子類就是基于CAS完成的

當兩個線程并發的線程執行++的時候,如果加限制,意味著這兩個++是串行的,能計算正確的,有時候者兩個++操作是穿插的,這個時候是會出現問題的

加鎖保證線程安全:通過鎖,強制避免出現穿插~~

原子類/CAS保證線程安全,借助CAS來識別當前是否出現穿插的情況,如果沒有穿插,此時直接修改就是安全的,如果出現了穿插,就會重新讀取內存中最新的值,再次嘗試修改。

部分源碼合起來的意思就是?

public int getAndIncrement(){int oldValue=value;       //先儲存值,防止別的線程偷摸修改之后,無法恢復到之前的值while(CAS(Value,oldValue,OldValue+1)!=true){  //檢查是否線程被別的偷摸修改了//上面的代碼是Value是否等于oldValue,假如等于就把Value賦值OldValue+1oldValue=value;                        //假如修改了就恢復了原來的樣子}return oldValue;}

?

假如這種情況,剛開始設置value=0,?

CAS是一個指令,這個指令本身是不能夠拆分的。

是否可能會出現,兩個線程,同時在兩個cpu上?微觀上并行的方式來執行,CAS本身是一個單個的指令,這里其實包含了訪問操作,當多個cpu嘗試訪問內存的時候,本質也是會存在先后順序的。

就算同時執行到CAS指令,也一定有一個線程的CAS先訪問到內存,另一個后訪問到內存

為啥CAS訪問內存會有先后呢?

多個CPU在操作同一個資源,也會涉及到鎖競爭(指令級別的鎖),是比我們平時說的synchronized代碼級別的鎖要輕量很多(cpu內部實現的機制)?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/38092.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/38092.shtml
英文地址,請注明出處:http://en.pswp.cn/news/38092.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

用身邊統計學告訴大家ChatGPT聽聞過的、用過的、重度使用的大概有多少?

最近摸魚時&#xff0c;看到有人發帖說信息繭房真的是無處不在。討論一番后&#xff0c;確實是這樣&#xff0c;每個人都在不同程度的“坐井觀天”罷了。拿最近火遍全球的ChatGPT來說&#xff0c;身邊真的不少人聽都沒聽過。這里有個更詳細調查&#xff0c;不過是Sunergia做的&…

全排列——力扣46

文章目錄 題目描述解法:回溯題目描述 解法:回溯 //version 1 vector<vector<int>> permute(<

分類預測 | MATLAB實現GWO-BiGRU-Attention多輸入分類預測

分類預測 | MATLAB實現GWO-BiGRU-Attention多輸入分類預測 目錄 分類預測 | MATLAB實現GWO-BiGRU-Attention多輸入分類預測預測效果基本介紹程序設計參考資料 預測效果 基本介紹 1.GWO-BiGRU-Attention 數據分類預測程序 2.代碼說明&#xff1a;基于灰狼優化算法&#xff08;GW…

Java SE 學習筆記(十)—— 正則表達式

目錄 1 引言2 常用匹配規則2.1 字符類2.2 預定義的字符類2.3 貪婪的量詞 3 正則表達式匹配的 API4 正則表達式應用4.1 正則表達式常見應用案例4.2 正則表達式在字符串方法中的使用4.3 正則表達式爬取信息 1 引言 &#x1f60d; 正則表達式可以用一些規定的字符來制定規則&#…

I2C連續讀寫實現

IIC系列文章: (1)I2C 接口控制器理論講解 (2)I2C接口控制設計與實現 (3)I2C連續讀寫實現 文章目錄 前言一、 i2c_bit_shift 模塊分析二、 i2c_control 模塊實現三、 i2c_control 模塊仿真測試前言 上文的 i2c_bit_shift 模塊說完了,我們發現實現一個字節的寫操作還是可以實現…

【UniApp開發小程序】小程序首頁(展示商品、商品搜索、商品分類搜索)【后端基于若依管理系統開發】

文章目錄 界面效果界面實現工具js頁面首頁讓文字只顯示兩行路由跳轉傳遞對象將商品分為兩列顯示使用中劃線劃掉原價 后端商品controllerservicemappersql 界面效果 【說明】 界面中商品的圖片來源于閑魚&#xff0c;若侵權請聯系刪除關于商品分類頁面的實現&#xff0c;請在我…

MySQL基礎篇

一、MySQL基礎 黑窗口命令操作: 連接MySQL服務器&#xff1a;mysql -u用戶名 -p密碼 [-h數據庫服務器的IP地址 -P端口號] ? -h 參數不加&#xff0c;默認連接的是本地 127.0.0.1 的MySQL服務器 -P 參數不加&#xff0c;默認連接的端口號是 3306 、 **上述指令&#xff0c;可…

建庫、建表、修改表、復制表、字符類型、數值類型、枚舉類型、日期時間類型、檢索目錄、數據導入命令、數據導入步驟、數據導出命令、非空、默認值、唯一索

Top NSD DBA DAY04 案例1&#xff1a;表管理案例2&#xff1a;數據類型案例3&#xff1a;數據批量處理案例4&#xff1a;表頭基本約束 1 案例1&#xff1a;表管理 1.1 問題 建庫練習建表練習修改表練習 1.2 方案 在MySQL50主機完成練習。 1.3 步驟 實現此案例需要按照如…

升級版“斯坦福AI小鎮”來了,這次的AI Agents有點不一樣

文娛是大模型落地的一個重要方向。 數科星球原創 作者丨苑晶 編輯丨大兔 八月中旬&#xff0c;AIGC游戲的風潮撲面而來。在游戲大廠按捺不住投入巨資的背景下&#xff0c;數科星球&#xff08;ID&#xff1a;digital-planet&#xff09;接觸到了多名業內精英也投身于此。人工…

Kafka第一課概述與安裝

生產經驗 面試重點 Broker面試重點 代碼,開發重點 67 章了解 如何記錄行為數據 1. Kafka概述 1.產生原因 前端 傳到日志 日志傳到Flume 傳到HADOOP 但是如果數據特比大&#xff0c;HADOOP就承受不住了 2.Kafka解決問題 控流消峰 Flume傳給Kafka 存到Kafka Hadoop 從Kafka…

Qt掃盲-Qt Paint System 概述

Qt Paint System 概述 一、概述二、繪圖設備和后端1. Widget2. Image3. Pixmap4. OpenGL繪制設備5. Picture6. 自定義繪制后端 三、繪圖與填充1. Drawing2. 填充 Filling 四、坐標系統1. 渲染Window-Viewport轉換 五、讀寫圖像文件1. QMovie 六、繪圖相關設備 一、概述 Qt的pa…

【數據庫】P2 SELECT 與 SQL注釋

SELECT 檢索單個列檢索多個列檢索所有列不重復的結果 DISTINCT限制結果 LIMIT 與 OFFSET注釋行內注釋多行注釋 檢索單個列 從 Products 表中檢索一個名為 prod_name 的列&#xff1b; SELECT prod_name FROM Products;【1】返回的數據可能是無序的&#xff0c;除非規定了順序…

7.5.tensorRT高級(2)-RAII接口模式下的生產者消費者多batch實現

目錄 前言1. RAII接口模式封裝生產者消費者2. 問答環節總結 前言 杜老師推出的 tensorRT從零起步高性能部署 課程&#xff0c;之前有看過一遍&#xff0c;但是沒有做筆記&#xff0c;很多東西也忘了。這次重新擼一遍&#xff0c;順便記記筆記。 本次課程學習 tensorRT 高級-RAI…

原生JS手寫掃雷小游戲

場景 實現一個完整的掃雷游戲需要一些復雜的邏輯和界面交互。我將為你提供一個簡化版的掃雷游戲示例&#xff0c;幫助你入門。請注意&#xff0c;這只是一個基本示例&#xff0c;你可以根據自己的需求進行擴展和改進。 思路 創建游戲板&#xff08;Grid&#xff09;&#xff1…

軟考:中級軟件設計師:文件管理,索引文件結構,樹型文件結構,位示圖,數據傳輸方式,微內核

軟考&#xff1a;中級軟件設計師: 提示&#xff1a;系列被面試官問的問題&#xff0c;我自己當時不會&#xff0c;所以下來自己復盤一下&#xff0c;認真學習和總結&#xff0c;以應對未來更多的可能性 關于互聯網大廠的筆試面試&#xff0c;都是需要細心準備的 &#xff08;1…

小森動畫回憶錄(二)-瀏覽哆啦a夢的四次元口袋

// DoraemonProps結構用于存儲單個道具信息 struct DoraemonProps{// 道具名稱string name;// 道具用途string UseOfProps; };// 從文件加載哆啦A夢道具信息到vector void LoadDoraemonProps(vector<DoraemonProps>& DoraemonProps) {// 創建文件輸入流ifstream str…

人臉識別技術應用安全管理規定(試行)

近年來&#xff0c;人臉識別技術不斷成熟&#xff0c;已大量應用于治安管理、金融支付、門禁考勤等諸多領域&#xff0c;極大便捷了公眾生活。然而&#xff0c;人臉識別技術在得到廣泛應用的同時&#xff0c;仍存在一些不規范現象。人臉識別因其技術特點&#xff0c;涉及公眾敏…

node.js 基礎高并發案例

什么是高并發 高并發是指系統在同一時間段內需要處理大量的并發請求或同時進行大量的操作。在計算機領域中&#xff0c;高并發通常指的是在短時間內有大量的用戶或客戶端同時訪問系統或進行操作&#xff0c;對系統的并發處理能力提出了較高的要求。 高并發的特點包括 大量的…

Python學習筆記第五十五天(Pandas CSV文件)

Python學習筆記第五十五天 Pandas CSV 文件read_csv()to_string()to_csv() 數據處理head()tail()fillna() info() 后記 Pandas CSV 文件 CSV&#xff08;Comma-Separated Values&#xff0c;逗號分隔值&#xff0c;有時也稱為字符分隔值&#xff0c;因為分隔字符也可以不是逗號…

【嵌入式學習筆記】嵌入式入門7——IIC總線協議

1.IIC簡介 IIC即Inter Integrated Circuit&#xff0c;集成電路總線&#xff0c;是一種同步&#xff0c;串行&#xff0c;半雙工通信總線。 IIC總線協議——總線就是傳輸數據通道&#xff0c;協議就是傳輸數據的規則&#xff0c;有以下特點&#xff1a; 由時鐘線SCL和數據線S…