為什麼Java有了synchronized之後還造了Lock鎖這個輪子?

来源:https://www.cnblogs.com/zzyang/archive/2022/05/26/16313085.html
-Advertisement-
Play Games

眾所周知,synchronized和Lock鎖是java併發編程中兩大利器,可以用來解決線程安全的問題。但是為什麼Java有了synchronized之後還是提供了Lock介面這個api,難道僅僅只是重覆造了輪子這麼簡單麽?本文就來探討一下這個問題。 談到這個問題,其實很多同學第一反應都會說,Loc ...


眾所周知,synchronized和Lock鎖是java併發編程中兩大利器,可以用來解決線程安全的問題。但是為什麼Java有了synchronized之後還是提供了Lock介面這個api,難道僅僅只是重覆造了輪子這麼簡單麽?本文就來探討一下這個問題。

談到這個問題,其實很多同學第一反應都會說,Lock鎖的性能比synchronized好,synchronized屬於重量級的鎖。但是在JDK 1.6版本之後,JDK對synchronized進行了一系列性能的優化,synchronized的性能其實有了大大的提升(如果不清楚的同學可以看一下 synchronized真的很重麽?這篇文章,文章內詳細的說明JDK對synchronized做了哪些優化),那麼既然性能不是問題,那麼主要的問題是什麼呢?

synchronized搶占鎖的特性

我們先來看一下synchronized搶占鎖的特性。synchronized在搶占鎖的時候,如果搶占不到,線程直接就進入阻塞狀態了,而線程進入阻塞狀態,其實什麼也幹不了,也釋放不了線程已經占有的資源,並且也無法主動或者被動打斷阻塞獲取鎖的操作,只有等別的線程釋放鎖之後才會被喚醒來重新獲取鎖。

synchronized阻塞獲取鎖產生的問題

那synchronized這種獲取鎖阻塞的特性,有什麼問題麽?其實有一種很重要的問題,那就是會產生死鎖的問題。

那什麼是死鎖?死鎖是指兩個或者兩個以上的線程在執行的過程中,因爭奪資源產生的一種互相等待的現象。

舉個例子來說,線程1先對加A加鎖,線程2對B加鎖。代碼運行到某一時刻,線程1需要對B加鎖,但是此時B的鎖已經被線程2占有,於是線程1就會阻塞,與此同時線程2同時也需要對A加鎖,發現A已經被線程1持有,也會進入阻塞,於是線程1和線程2都在等對方釋放資源,就產生了死鎖的問題,並且由於synchronized阻塞的特性,線程無法主動或者被動停止阻塞,勢必會導致這個死鎖永遠無法通過主動或者人為干預(其它線程干預)來解決。

那麼有什麼好的辦法來解決阻塞導致死鎖的問題呢?

我們分析一下死鎖產生的問題主要是線程都在相互等待其它線程釋放資源導致的,基於這個問題我們思考一下,如果一個線程獲取不到鎖,然後就停止獲取鎖,不阻塞,或者是阻塞一會就不再阻塞,又或是阻塞過程中被其他線程打斷,那樣這是不是就不是產生死鎖的問題了。

就拿上面的例子來說,假設線程1獲取B的阻塞鎖超過一定時間,主動放棄獲取B的鎖,那麼線程1代碼就可以繼續往下執行,當執行完之後,線程1釋放了A鎖,此時線程2就能獲取到A的鎖,那麼線程2就可以繼續執行了,這樣是不是死鎖的問題就完美解決了。

其實Lock鎖就提供了上述提到的幾種解決方案的api,接下來我們就來看看Lock鎖提供的api。

Lock鎖

 void lockInterruptibly() throws InterruptedException;

阻塞可以被打斷的加鎖方法,這是一個被動放棄獲取鎖的方法。就是說其它線程主動當調用阻塞線程的interrupt方法之後,該阻塞線程就會放棄繼續獲取鎖,然後拋出InterruptedException 異常,所以對於使用方來說,只要捕獲這個異常,就能保證線程的代碼繼續執行了。

boolean tryLock();

這個方法是嘗試加鎖,加鎖失敗後就放棄加鎖,不會阻塞,直接返回false。

 boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

這個方法相比上面的就是嘗試加鎖失敗後在阻塞的一定時間之後,如果還沒有獲取到鎖,那麼就放棄獲取鎖。

Lock介面的實現有很多,但基本上都是基於Java的AQS的實現來完成的。AQS其實主要是維護了一個鎖的狀態欄位state和一個雙向鏈表。當線程獲取鎖失敗之後,就會加入到雙向鏈表中,然後阻塞或者不阻塞,這得看具體的方法實現。

Lock介面的一個實現ReentrantLock就是基於AQS實現來講的,這裡就不繼續展開講解ReentrantLock的實現原理,如果有感興趣的同學,可以看一下 一文帶你看懂Java中的Lock鎖底層AQS到底是如何實現的 這篇文章,文章是基於ReentrantLock來講解AQS的加鎖和釋放鎖的原理。

總結

好了,到這裡其實大家應該知道了,為什麼需要Lock鎖,因為synchronized獲取不到鎖的時候會阻塞,並且阻塞不可被打斷的特性會導致可能會產生死鎖的問題,為瞭解決這個問題,Java就提供了Lock鎖的實現,從主動放棄獲取鎖或者被動放棄獲取鎖的方式,解決一直阻塞可能產生的死鎖問題。

如果覺得這篇文章對你有所幫助,還請幫忙點贊、在看、轉發一下,碼字不易,非常感謝!

如果你想聯繫我,歡迎關註我的個人的微信公眾號三友的java日記,每天都會發佈技術性的文章,期待與你一起進步。

往期熱門文章推薦

 掃碼關註公眾號,及時乾貨不錯過,公眾號致力於通過畫圖加上通俗易懂的語言講解技術,讓技術更加容易學習。 

 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 服務演變之路 單體應用架構 在剛開始的時候,企業的用戶量、數據量規模都⽐較⼩,項⽬所有的功能模塊都放在⼀個⼯程中編碼、編譯、打包並且部署在⼀個Tomcat容器中的架構模式就是單體應用架構,這樣的架構既簡單實用、便於維護,成本⼜低,成為了那個時代的主流架構⽅式。這時候由於業務以及規模都⽐較⼩,所以⽆論 ...
  • 在後面的漏洞研究的學習中,必須要會的幾個知識點。反射機制和動態代理機制。至於反射的前面已經講到過了,這裡就不做更多的贅述了。反射是通過class文件去獲取對象對象的方法. ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 場景說明 現有一個 10G 文件的數據,裡面包含了 18-70 之間的整數,分別表示 18-70 歲的人群數量統計,假設年齡範圍分佈均勻,分別表示系統中所有用戶的年齡數,找出重覆次數最多的那個數,現有一臺記憶體為 4G、2 核 CPU 的電腦,請寫一個演算法實現。 23,31,42,19,60,30,3 ...
  • Conda 創建 Python 虛擬環境不純凈的問題(2021.1.18) 1. 產生環境 Ubuntu 16.04; Conda 4.9.2; Python 3.6; 2. 問題描述 通過 Conda 命令創建 Python 虛擬環境後,利用 pip list 命令查看包列表,發現有很多多餘的包, ...
  • SpringBoot MySQL密碼等敏感信息加密方案(2021.04.27) 一、背景說明 SpringBoot 項目經常將連接資料庫的密碼明文放在配置文件里,安全性就比較低,尤其一些企業對安全性要求很高,因此我們就考慮如何對密碼等敏感信息進行加密。 二、解決方案 通過 Jasypt 對密碼等敏感 ...
  • IO問題一直是面試的重災區之一 但又是非常重要而且面試必問的知識點 一個工作了7年的粉絲私信我,他去面試了 4家互聯網公司, 有三個公司問他網路IO的問題,另外一個公司問了Netty,結果都沒回答上來。 好吧,對於“IO和NIO的區別”,看看普通人和高手的回答。 普通人: 嗯。。。。。。。。。。 高 ...
  • 0、重寫博文的原因 當初我的SpringBoot系列的知識是採用分節來寫的,即:每一個知識點為一篇博文,但是:最近我霉到家了,我發現有些博文神奇般地打不開了,害我去找當初的markdown筆記,但是方便的話還是線上版舒服,只要有網就可以訪問,因此昨天晚上東拼西湊搞出了這篇SpringBoot基礎系列 ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...