Java多線程(4)---死鎖和Synchronized加鎖流程

目錄

前言

一.synchronized

1.1概念?

1.2Synchronized是什么鎖?

1.3Synchronized加鎖工作過程

1.4其他優化操作

二.死鎖

2.1什么是死鎖

2.2死鎖的幾個經典場景

2.3死鎖產生的條件

2.4如何解決死鎖


🎁個人主頁:tq02的博客_CSDN博客-C語言,Java,Java數據結構領域博主
🎥 本文由 tq02 原創,首發于 CSDN🙉
🎄?本章講解內容:線程的策略鎖、CAS和JUC

🎥多線程學習專欄:多線程學習專欄

🎥其他學習專欄:??C語言?? ? ? ??JavaSE?? ? ??MySQL基礎?

前言

? ? ? ? 在多線程的講解當中,我們可以知道synchronized是加鎖操作,讓兩個線程發生互斥效果,在代碼中使用synchronized關鍵字來實現鎖的獲取和釋放。如果是剛剛接觸多線程的人,我希望你可以從第一章多線程開始學習:http://t.csdn.cn/0vEhY

一.synchronized

1.1概念?

????????Synchronized是Java中內置的鎖機制,用于實現線程同步。它可以通過在代碼中使用synchronized關鍵字來實現鎖的獲取和釋放。Synchronized關鍵字可以用在方法上或者代碼塊中。當一個線程執行到synchronized修飾的代碼塊時,它會嘗試獲取鎖,如果鎖沒有被其他線程占用,則獲取成功,執行代碼塊中的內容。如果鎖已經被其他線程占用,則該線程會進入等待狀態,直到獲取到鎖才能繼續執行。

1.2Synchronized是什么鎖?

  1. 開始時是樂觀鎖, 如果鎖沖突頻繁, 就轉換為悲觀鎖.
  2. 開始是輕量級鎖實現, 如果鎖被持有的時間較長, 就轉換成重量級鎖.
  3. 實現輕量級鎖的時候大概率用到的自旋鎖策略
  4. 是一種不公平鎖
  5. 是一種可重入鎖
  6. 不是讀寫鎖
    ?

注:需要使用公平鎖,建議使用ReentrantLock來實現。ReentrantLock提供了公平鎖和非公平鎖兩種模式,通過構造函數的參數來指定鎖的模式。

1.3Synchronized加鎖工作過程

????????對于鎖資源只有一個或者兩個線程交替競爭的,仍然需要使用系統調用,無疑對CPU資源是極大的消耗。因此,在jdk1.6針對Synchronized加鎖進行了優化。按對鎖的競爭程度劃分成:無鎖,偏向鎖,輕量級鎖,重量級鎖。簡單而言就是從無鎖-->重量級鎖。?

無鎖

當你添加了鎖時,如果編譯器認為不需要加鎖,會自動刪除,因此便是無鎖

偏向鎖

偏向鎖不是真的 "加鎖", 只是給對象頭中做一個 "偏向鎖的標記", 記錄這個鎖屬于哪個線程.
如果后續沒有其他線程來競爭該鎖, 那么就不用進行其他同步操作了(避免了加鎖解鎖的開銷)
如果后續有其他線程來競爭該鎖(剛才已經在鎖對象中記錄了當前鎖屬于哪個線程了, 很容易識別當前申請鎖的線程是不是之前記錄的線程), 那就取消原來的偏向鎖狀態, 進入一般的輕量級鎖狀態.
注:相當于做個標記,相當于 "延遲加鎖" . 能不加鎖就不加鎖, 盡量避免不必要的加鎖開銷.

輕量級鎖
隨著其他線程進入競爭, 偏向鎖狀態被消除, 進入輕量級鎖狀態(自適應的自旋鎖).
此處的輕量級鎖就是通過 CAS 來實現

  1. 通過 CAS 檢查并更新一塊內存 (比如 null => 該線程引用)
  2. 如果更新成功, 則認為加鎖成功
  3. 如果更新失敗, 則認為鎖被占用, 繼續自旋式的等待(并不放棄 CPU).

注:此處的自旋鎖不會一種持續進行,而是達到一定的時間/重試次數, 就不再自旋了.

重量級鎖
????????如果鎖競爭進一步激烈, 自旋不能快速獲取到鎖狀態, 就會膨脹為重量級鎖

此處的重量級鎖就是指用到內核提供的 mutex .
具體流程:

  • 執行加鎖操作, 先進入內核態.
  • 在內核態判定當前鎖是否已經被占用
  • 如果該鎖沒有占用, 則加鎖成功, 并切換回用戶態.
  • 如果該鎖被占用, 則加鎖失敗. 此時線程進入鎖的等待隊列, 掛起. 等待被操作系統喚醒.
  • 經歷了一系列的滄海桑田, 這個鎖被其他線程釋放了, 操作系統也想起了這個掛起的線程, 于是喚醒
  • 這個線程, 嘗試重新獲取鎖

1.4其他優化操作

? ? ? ? 我們額外補充2個編譯器對鎖的優化操作。鎖消除和鎖粗化

鎖消除

????????代碼中, 用到了 synchronized, 但其實沒有在多線程環境下. (例如 StringBuffer)

StringBuffer tq02 = new StringBuffer();
tq02.append("a");
tq02.append("b");
tq02.append("c");
tq02.append("d");

每個 append 的調用都會涉及加鎖和解鎖. 但如果只是在單線程中執行這個代碼, 那么這些加
鎖解鎖操作是沒有必要的, 白白浪費了一些資源開銷.因此將鎖給優化了。

鎖粗化

鎖的粗化是根據鎖的粒度:粗和細

?實際開發過程中, 使用細粒度鎖, 是期望釋放鎖的時候其他線程能使用鎖.但可能并沒有其他線程來搶占這個鎖. 這種情況 JVM 就會自動把鎖粗化, 避免頻繁申請釋放鎖.
?

二.死鎖

2.1什么是死鎖

????????死鎖是指在多進程系統中,每個進程都在等待某個資源,而該資源又被其他進程占用,導致所有進程都無法繼續執行的狀態。

例如:A、B、C、D和E去上廁所,A進入廁所并且鎖門,B.C.D等待,可是A剛剛進入廁所,因為特殊的原因,憑空轉移到了外面,A就得重新排隊,可是門還是鎖著的啊,因此導致了死鎖。

2.2死鎖的幾個經典場景

經典場景有:

  1. 一個線程,一把鎖
  2. 兩個線程,兩把鎖
  3. 多個線程,多把鎖

1.一個線程,一把鎖

? ? ? ? 一個線程連續被同一個加鎖兩次,如果是不可重入鎖,那么會是死鎖。

解析:我去上廁所,我把廁所門鎖住,再通過廁所的窗戶出去,然后再來上廁所,發現廁所鎖住了,就耐心等待,卻沒想過這是自己鎖的。

代碼實現:

public class Counter {void increase() {       synchronize(this){increase()   //可以理解為翻窗逃走,第二次加鎖時,是鎖了的}
}public static void main(String[] args) throws InterruptedException {final Counter counter = new Counter();Thread t1 = new Thread(() -> {counter.increase();});t1.start();}
}

2.兩個線程,兩把鎖

????????線程1先獲取鎖A,再嘗試獲取鎖B,同時,線程2先獲取鎖B,再嘗試獲取鎖A,此時兩個線程就會互相僵住,誰都獲取不到對方持有的鎖。

解析:我在汽車里,車鑰匙在我妻子手上,我出不來,我妻子在房間里,房間鑰匙在我手上,我妻子也出不來,導致雙方被鎖,導致死鎖。

代碼示例:

public class Test {public static void main(String[] args) {//2個鎖對象Object lockerA = new Object();Object lockerB = new Object();Thread t1 = new Thread(() -> {System.out.println("t1嘗試獲取鎖A");synchronized (lockerA){System.out.println("t1獲取到鎖A");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t1嘗試獲取鎖B");synchronized (lockerB){System.out.println("t1獲取到鎖B");}}});Thread t2 = new Thread(() -> {System.out.println("t2嘗試獲取鎖B");synchronized (lockerB){System.out.println("t2獲取到鎖B");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("t2嘗試獲取鎖A");synchronized (lockerA){System.out.println("t2獲取到鎖A");}}});t1.start();t2.start();}
}

3.多個線程,多把鎖

? ? ? ? 很明顯啊,兩把鎖,兩個線程也有問題,更何況是多把鎖啊,在這方面最經典的是"哲學家就餐問題"。

如圖:火柴人是哲學家、紅線是筷子,每一個哲學家的左右都有一根筷子。規定,當有一根哲學家餓了,會先拿起左邊的筷子,然后再拿右邊的筷子,吃完了就放下筷子。

造成死鎖問題:每一個哲學家都餓了,然后都拿起了左邊的筷子,可是當拿右邊的筷子時,發現有其他人在使用,所以導致阻塞,然后一直等待別人吃飽放下筷子,可是每個人都在等待。

2.3死鎖產生的條件

死鎖產生需要四個條件:

  1. 互斥使用,即當資源被一個線程使用(占有)時,別的線程不能使用
  2. 不可搶占,資源請求者不能強制從資源占有者手中奪取資源,資源只能由資源占有者主動釋放。
  3. 請求和保持,即當資源請求者在請求其他的資源的同時保持對原有資源的占有。
  4. 循環等待,即存在一個等待隊列:P1占有P2的資源,P2占有P3的資源,P3占有P1的資源。這樣就形成了一個等待環路。

當上述四個條件都成立的時候,便形成死鎖。當然,死鎖的情況下如果打破上述任何一個條件,便可讓死鎖消失。
?

2.4如何解決死鎖

? ? ? ? 想沒有死鎖,那么我們可以從死鎖產生的條件入手,只有破壞其他一條就可以了。?互斥使用和不可搶占是鎖的基本特性,因此無法干預,但是請求和保持,也不可能改變,因為這是代碼執行邏輯。因此只有循環等待,我們可以打破

????????為了解決死鎖問題,可以采取預防、避免、檢測和解除四種方法。

預防:通過設置某些限制條件,以防止死鎖的發生。

避免:系統在分配資源時根據資源的使用情況提前作出預測,從而避免死鎖的發生。

檢測:允許系統在運行過程中產生死鎖,但系統中有相應的管理模塊可以及時檢測出已經產生的死鎖,并精確地確定與死鎖有關的進程和資源,然后采取適當措施清除系統中已經產生的死鎖。

解除:當發現有進程死鎖后,立即解脫它從死鎖狀態中出來。常用的方法包括剝奪資源和撤銷進程。剝奪資源是從其他進程中剝奪足夠數量的資源給死鎖進程,以解除死鎖狀態。撤銷進程可以直接撤銷死鎖進程或撤銷代價最小的進程,直至有足夠的資源可用,從而消除死鎖狀態。


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ????????????????---------------------懶惰的tq02

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

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

相關文章

設計模式 : 單例模式筆記

文章目錄 一.單例模式二.單例模式的兩種實現方式餓漢模式懶漢模式 一.單例模式 一個類只能創建一個對象,這樣的類的設計模式就稱為單例模式,該模式保證系統中該類只能有一個實例(并且父子進程共享),一個很典型的單例類就是CSTL的內存池C單例模式的基本設計思路: 私有化構造函數…

PyTorch翻譯官網教程-LANGUAGE MODELING WITH NN.TRANSFORMER AND TORCHTEXT

官網鏈接 Language Modeling with nn.Transformer and torchtext — PyTorch Tutorials 2.0.1cu117 documentation 使用 NN.TRANSFORMER 和 TORCHTEXT進行語言建模 這是一個關于訓練模型使用nn.Transformer來預測序列中的下一個單詞的教程。 PyTorch 1.2版本包含了一個基于論…

Shell編程——弱數據類型的腳本語言快速入門指南

目錄 Linux Shell 數據類型 變量類型 運算符 算術運算符 賦值運算符 拼接運算符 比較運算符 關系運算符 控制結構 順序結構 條件分支結構 if 條件語句 case 分支語句 循環結構 for 循環 while 循環 until 循環 break 語句 continue語句 函數 函數定義 …

Stable Diffusion Webui源碼剖析

1、關鍵python依賴 (1)xformers:優化加速方案。它可以對模型進行適當的優化來加速圖片生成并降低顯存占用。缺點是輸出圖像不穩定,有可能比不開Xformers略差。 (2)GFPGAN:它是騰訊開源的人臉修…

大數據掃盲(1): 數據倉庫與ETL的關系及ETL工具推薦

在數字化時代,數據成為了企業決策的關鍵支持。然而,隨著數據不斷增長,有效地管理和利用這些數據變得至關重要。數據倉庫和ETL工具作為數據管理和分析的核心,將幫助企業從龐雜的數據中提取有價值信息。 一、ETL是什么? …

【不限于聯想Y9000P電腦關蓋再打開時黑屏的解決辦法】

不限于聯想Y9000P電腦關蓋再打開時黑屏的解決辦法 問題的前言問題的出現問題擬解決 問題的前言 事情發生在昨天,更新了Win11系統后: 最惹人注目的三處地方就是: 1.可以查看時間的秒數了; 2.右鍵展示的內容變窄了; 3.按…

Pycharm 雙擊啟動失敗?

事故 雙擊 Pycharm 后,出現加載工程,我不想加載這個工程,就點擊了彈出的 cancle 取消按鈕。然后再到桌面雙擊 Pycharm 卻發現無法啟動了。哪怕以管理員權限運行也沒用,就是不出界面。 原因未知 CtrlshiftESC 打開后臺&#xff…

【騰訊云 Cloud Studio 實戰訓練營】Hexo 框架 Butterfly 主題搭建個人博客

什么是Cloud Studio Cloud Studio 是基于瀏覽器的集成式開發環境(IDE),為開發者提供了一個永不間斷的云端工作站。用戶在使用 Cloud Studio 時無需安裝,隨時隨地打開瀏覽器就能在線編程。 ? Hexo 博客成品展示 本人博客如下&…

leetcode268. 丟失的數字

這題簡單的有點過分了吧。。。 一開始還納悶會不會有重復的元素,后來看到[0,n]范圍,那么肯定有n1個數字,然后要在n 個數字里面找誰沒有,那肯定沒有重復的元素,如果有重復,就不止缺少一個元素了。 思路&am…

【Spring】-Spring項目的創建

作者:學Java的冬瓜 博客主頁:?冬瓜的主頁🌙 專欄:【Framework】 主要內容:創建spring項目的步驟:先創建一個maven項目,再在pom.xml中添加spring框架支持,最后寫一個啟動類。 文章目…

Field injection is not recommended

文章目錄 1. 引言2. 不推薦使用Autowired的原因3. Spring提供了三種主要的依賴注入方式3.1. 構造函數注入(Constructor Injection)3.2. Setter方法注入(Setter Injection)3.3. 字段注入(Field Injection) 4…

03 QT基本控件和功能類

一 進度條 、水平滑動條 垂直滑動條 當在QT中,在已知類名的情況下,要了解類的構造函數 常用屬性 及 信號和槽 常用api 特征:可以獲取當前控件的值和設置它的當值 ---- int ui->progressBar->setValue(value); //給進度條設置一個整型值 ui->progressBar->value…

計算機視覺五大核心研究任務全解:分類識別、檢測分割、人體分析、三維視覺、視頻分析

目錄 一、引言1.1 計算機視覺的定義1.1.1 核心技術1.1.2 應用場景 1.2 歷史背景及發展1.2.1 1960s-1980s: 初期階段1.2.2 1990s-2000s: 機器學習時代1.2.3 2010s-現在: 深度學習的革命 1.3 應用領域概覽1.3.1 工業自動化1.3.2 醫療圖像分析1.3.3 自動駕駛1.3.4 虛擬現實與增強現…

【Linux】進程調度

進程調度 硬件向OS發送時間中斷 --> 系統時鐘硬件會進行時間計數,每隔一段很短的時間會向OS發送時鐘中斷,處理中斷,檢測進程時間片 --> 收到中斷,OS就會不斷定期地執行對應的時鐘中斷處理方法,檢查當前進程的時…

山東布谷科技直播軟件開發WebRTC技術:建立實時通信優質平臺

在數字化的時代,實時通信成為了人們遠程交流的主要方式,目前市場上也出現了很多帶有實時通信交流的軟件,實時通信符合人們現在的需求,所以在直播軟件開發過程中,開發者也運用了實時通信技術為直播軟件加入了實時通信的…

【計算機視覺|生成對抗】生成對抗網絡(GAN)

本系列博文為深度學習/計算機視覺論文筆記,轉載請注明出處 標題:Generative Adversarial Nets 鏈接:Generative Adversarial Nets (nips.cc) 摘要 我們提出了一個通過**對抗(adversarial)**過程估計生成模型的新框架…

mybatisplus學習筆記

1.踩過的坑 1.MybatisPlus 要與其代碼生成器的版本一致; 2.要使用新版代碼(3.5.1及以上)生成器則要使用springboot3,如果用springboot2使用新版代碼生成器會導致builder.parent(“com.sdfsf”) // 設置父包名》重復!&…

2.阿里云對象存儲OSS

1.對象存儲概述 文件上傳,是指將本地圖片、視頻、音頻等文件上傳到服務器上,可以供其他用戶瀏覽或下載的過程。文件上傳在項目中應用非常廣泛,我們經常發抖音、發朋友圈都用到了文件上傳功能。 實現文件上傳服務,需要有存儲的支持…

【概念理解】STM32中的sprintf()函數

sprintf()函數 這個函數在 stdio.h中;可以將格式化的數據寫入到一個字符串緩沖區中。 int sprintf(char *str, const char *format, ...);str:指向字符數組的指針,即用于存儲格式化后字符串的緩沖區。format:格式化字符串&#…

(十六)大數據實戰——安裝使用mysql版的hive服務

前言 hive默認使用的是內嵌據庫derby,Derby 是一個嵌入式數據庫,可以輕松地以庫的形式集成到應用程序中。它不需要獨立的服務器進程,所有的數據存儲在應用程序所在的文件系統中。為了支持hive服務更方便的使用,我們使用mysql數據…