摘要: 在Java中提供了synchronized關鍵字來保證只有一個執行緒能夠存取同步程式碼區塊。既然已經提供了synchronized關鍵字,為何在Java的SDK包中,還會提供Lock介面呢?這是不是重複造輪子,多此一舉呢?今天,我們就一起來探討下這個問題。
在Java中提供了synchronized
關鍵字來保證只有一個執行緒能夠存取同步程式碼區塊。既然已經提供了synchronized
關鍵字,那為何在Java的SDK套件中,還會提供Lock介面呢?這是不是重複造輪子,多此一舉呢?今天,我們就一起來探討下這個問題。
問題?
既然JVM中提供了synchronized關鍵字來保證只有一個執行緒能夠存取同步程式碼區塊,為何還要提供Lock介面呢?這是在重複造輪子嗎? Java的設計者為何要這樣做呢?讓我們一起帶著疑問往下看。
一、為何提供Lock介面?
很多小夥伴可能會聽說過,在Java 1.5版本中,synchronized的效能不如Lock,但在Java 1.6版本之後,synchronized
做了很多優化,效能提升了不少。那既然synchronized關鍵字的效能已經提升了,那為何還要使用Lock呢?
如果我們向更深層次思考的話,就不難想到了:我們使用synchronized
加鎖是無法主動釋放鎖的,這就會涉及到死鎖的問題。
二、死鎖問題
如果要發生死鎖,則必須存在以下四個必要條件,四者缺一不可。
#互斥條件
在一段時間內某資源僅為一個執行緒所佔有。此時若有其他執行緒請求該資源,則請求執行緒只能等待。
不可剝奪條件
#執行緒所獲得的資源在未使用完畢之前,不能被其他線程強行奪走,即只能由獲得該資源的線程自己來釋放(只能是主動釋放)。
請求與保持條件
#執行緒已經保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他執行緒佔有,此時請求執行緒被阻塞,但對自己已獲得的資源保持不放。
循環等待條件
#在發生死鎖時必然存在一個行程等待佇列{P1,P2,&hellip ;,Pn},其中P1等待P2佔有的資源,P2等待P3佔有的資源,…,Pn等待P1佔有的資源,形成一個進程等待環路,環路中每一個進程所佔有的資源同時被另一個申請,也就是前一個進程佔有後一個進程所深情地資源。
三、synchronized的限制
如果我們的程式使用synchronized
關鍵字發生了死鎖時,synchronized關鍵是無法破壞「不可剝奪」這個死鎖的條件的。這是因為synchronized申請資源的時候, 如果申請不到, 線程直接進入阻塞狀態了, 而線程進入阻塞狀態, 啥都乾不了, 也釋放不了線程已經佔有的資源。
然而,在大部分場景下,我們都是希望「不可剝奪」這個條件能夠被破壞。也就是說對於「不可剝奪」這個條件,佔用部分資源的線程進一步申請其他資源時, 如果申請不到, 可以主動釋放它佔有的資源, 這樣不可剝奪這個條件就破壞掉了。
如果我們自己重新設計鎖定來解決synchronized
的問題,我們該如何設計呢?
四、解決問題
了解了synchronized的限制之後,如果是讓我們自己實作一個同步鎖,我們該如何設計呢?也就是說,我們在設計鎖的時候,要如何解決synchronized的限制問題呢?這裡,我覺得可以從三個面向來思考這個問題。
(1)能夠回應中斷。 synchronized
的問題是, 持有鎖定A後, 如果嘗試取得鎖定B失敗, 那麼執行緒就進入阻塞狀態, 一旦發生死鎖, 就沒有任何機會來喚醒阻塞的執行緒。但如果阻塞狀態的執行緒能夠回應中斷訊號, 也就是說當我們給阻塞的執行緒發送中斷訊號的時候, 能夠喚醒它, 那它就有機會釋放曾經持有的鎖定A。這樣就破壞了不可剝奪條件了。
(2)支援逾時。如果執行緒在一段時間之內沒有取得到鎖, 不是進入阻塞狀態, 而是回傳一個錯誤, 那這個執行緒也有機會釋放曾經持有的鎖。這樣也能破壞不可剝奪條件。
(3)非阻塞地取得鎖。如果嘗試取得鎖定失敗, 並不進入阻塞狀態, 而是直接返回, 那這個線程也有機會釋放曾經持有的鎖。這樣也能破壞不可剝奪條件。
體現在Lock介面上,就是Lock介面提供的三個方法,
如下:
// 支持中断的API void lockInterruptibly() throws InterruptedException; // 支持超时的API boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 支持非阻塞获取锁的API boolean tryLock();
lockInterruptibly()
支援中斷。
tryLock()方法
#tryLock()方法是有回傳值的,它表示用來嘗試取得鎖,如果取得成功,則回傳true,如果取得失敗(即鎖已被其他執行緒取得),則傳回false,也就說這個方法無論如何都會立即回傳。在拿不到鎖時不會一直在那裡等待。
tryLock(long time, TimeUnit unit)方法
tryLock
(long time, TimeUnit unit)方法和tryLock ()方法是類似的,只不過差別在於這個方法在拿不到鎖時會等待一定的時間,在時間期限之內如果還拿不到鎖,就回傳false。如果一開始拿到鎖或在等待期間內拿到了鎖,則回傳true。
也就是說,對於死鎖問題,Lock能夠破壞不可剝奪的條件,例如,我們下面的程式碼就破壞了死鎖的不可剝奪的條件。
public class TansferAccount{ private Lock thisLock = new ReentrantLock(); private Lock targetLock = new ReentrantLock(); //账户的余额 private Integer balance; //转账操作 public void transfer(TansferAccount target, Integer transferMoney){ boolean isThisLock = thisLock.tryLock(); if(isThisLock){ try{ boolean isTargetLock = targetLock.tryLock(); if(isTargetLock){ try{ if(this.balance >= transferMoney){ this.balance -= transferMoney; target.balance += transferMoney; } }finally{ targetLock.unlock } } }finally{ thisLock.unlock(); } } } }
例外,Lock下面有一個ReentrantLock
,而ReentrantLock
支援公平鎖和非公平鎖。
在使用ReentrantLock的時候, ReentrantLock中有兩個建構函數, 一個是無參建構函數, 一個是傳入fair參數的建構子。 fair參數代表的是鎖的公平策略, 如果傳入true就表示需要建構一個公平鎖, 反之則表示要建構一個非公平鎖。如下程式碼片段所示。
//无参构造函数: 默认非公平锁 public ReentrantLock() { sync = new NonfairSync(); } //根据公平策略参数创建锁 public ReentrantLock(boolean fair){ sync = fair ? new FairSync() : new NonfairSync(); }
鎖定的實作在本質上都對應著一個入口等待佇列, 如果一個執行緒沒有取得鎖, 就會進入等待佇列, 當有執行緒釋放鎖的時候, 就需要從等待佇列中喚醒一個等待的線程。如果是公平鎖, 喚醒的策略是誰等待的時間長, 就喚醒誰, 很公平; 如果是非公平鎖, 則不提供這個公平保證, 有可能等待時間短的線程反而先被喚醒。而Lock是支持公平鎖的,synchronized不支援公平鎖。
最後,值得注意的是,在使用Lock加鎖時,一定要在finally{}
程式碼區塊中釋放鎖,例如,下面的程式碼片段所示。
try{ lock.lock(); }finally{ lock.unlock(); }
註:其他synchronized和Lock的詳細說明,小夥伴們自行查閱即可。
以上是Java中為什麼需要提供Lock,而不只使用synchronized關鍵字?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本文討論了使用Maven和Gradle進行Java項目管理,構建自動化和依賴性解決方案,以比較其方法和優化策略。

本文使用Maven和Gradle之類的工具討論了具有適當的版本控制和依賴關係管理的自定義Java庫(JAR文件)的創建和使用。

本文討論了使用咖啡因和Guava緩存在Java中實施多層緩存以提高應用程序性能。它涵蓋設置,集成和績效優勢,以及配置和驅逐政策管理最佳PRA

本文討論了使用JPA進行對象相關映射,並具有高級功能,例如緩存和懶惰加載。它涵蓋了設置,實體映射和優化性能的最佳實踐,同時突出潛在的陷阱。[159個字符]

Java的類上載涉及使用帶有引導,擴展程序和應用程序類負載器的分層系統加載,鏈接和初始化類。父代授權模型確保首先加載核心類別,從而影響自定義類LOA


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

SAP NetWeaver Server Adapter for Eclipse
將Eclipse與SAP NetWeaver應用伺服器整合。

禪工作室 13.0.1
強大的PHP整合開發環境

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

Dreamweaver CS6
視覺化網頁開發工具

MantisBT
Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。