自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java中提供了synchronized,為什么還要提供Lock呢?

開發(fā) 后端
在Java中提供了synchronized關(guān)鍵字來保證只有一個線程能夠訪問同步代碼塊。既然已經(jīng)提供了synchronized關(guān)鍵字,那為何在Java的SDK包中,還會提供Lock接口呢?這是不是重復(fù)造輪子,多此一舉呢?今天,我們就一起來探討下這個問題。

 [[342267]]

作者個人研發(fā)的在高并發(fā)場景下,提供的簡單、穩(wěn)定、可擴(kuò)展的延遲消息隊列框架,具有精準(zhǔn)的定時任務(wù)和延遲隊列處理功能。自開源半年多以來,已成功為十幾家中小型企業(yè)提供了精準(zhǔn)定時調(diào)度方案,經(jīng)受住了生產(chǎn)環(huán)境的考驗(yàn)。為使更多童鞋受益,現(xiàn)給出開源框架地址:

https://github.com/sunshinelyz/mykit-delay

寫在前面

在Java中提供了synchronized關(guān)鍵字來保證只有一個線程能夠訪問同步代碼塊。既然已經(jīng)提供了synchronized關(guān)鍵字,那為何在Java的SDK包中,還會提供Lock接口呢?這是不是重復(fù)造輪子,多此一舉呢?今天,我們就一起來探討下這個問題。

再造輪子?

既然JVM中提供了synchronized關(guān)鍵字來保證只有一個線程能夠訪問同步代碼塊,為何還要提供Lock接口呢?這是在重復(fù)造輪子嗎?Java的設(shè)計者們?yōu)楹我@樣做呢?讓我們一起帶著疑問往下看。

為何提供Lock接口?

很多小伙伴可能會聽說過,在Java 1.5版本中,synchronized的性能不如Lock,但在Java 1.6版本之后,synchronized做了很多優(yōu)化,性能提升了不少。那既然synchronized關(guān)鍵字的性能已經(jīng)提升了,那為何還要使用Lock呢?

如果我們向更深層次思考的話,就不難想到了:我們使用synchronized加鎖是無法主動釋放鎖的,這就會涉及到死鎖的問題。

死鎖問題

如果要發(fā)生死鎖,則必須存在以下四個必要條件,四者缺一不可。

 

互斥條件

在一段時間內(nèi)某資源僅為一個線程所占有。此時若有其他線程請求該資源,則請求線程只能等待。

不可剝奪條件

線程所獲得的資源在未使用完畢之前,不能被其他線程強(qiáng)行奪走,即只能由獲得該資源的線程自己來釋放(只能是主動釋放)。

請求與保持條件

線程已經(jīng)保持了至少一個資源,但又提出了新的資源請求,而該資源已被其他線程占有,此時請求線程被阻塞,但對自己已獲得的資源保持不放。

循環(huán)等待條件

在發(fā)生死鎖時必然存在一個進(jìn)程等待隊列{P1,P2,…,Pn},其中P1等待P2占有的資源,P2等待P3占有的資源,…,pn等待p1占有的資源,形成一個進(jìn)程等待環(huán)路,環(huán)路中每一個進(jìn)程所占有的資源同時被另一個申請,也就是前一個進(jìn)程占有后一個進(jìn)程所深情地資源。

synchronized的局限性

如果我們的程序使用synchronized關(guān)鍵字發(fā)生了死鎖時,synchronized關(guān)鍵是是無法破壞“不可剝奪”這個死鎖的條件的。這是因?yàn)閟ynchronized申請資源的時候, 如果申請不到, 線程直接進(jìn)入阻塞狀態(tài)了, 而線程進(jìn)入阻塞狀態(tài), 啥都干不了, 也釋放不了線程已經(jīng)占有的資源。

然而,在大部分場景下,我們都是希望“不可剝奪”這個條件能夠被破壞。也就是說對于“不可剝奪”這個條件,占用部分資源的線程進(jìn)一步申請其他資源時, 如果申請不到, 可以主動釋放它占有的資源, 這樣不可剝奪這個條件就破壞掉了。

如果我們自己重新設(shè)計鎖來解決synchronized的問題,我們該如何設(shè)計呢?

解決問題

了解了synchronized的局限性之后,如果是讓我們自己實(shí)現(xiàn)一把同步鎖,我們該如何設(shè)計呢?也就是說,我們在設(shè)計鎖的時候,要如何解決synchronized的局限性問題呢?這里,我覺得可以從三個方面來思考這個問題。

(1)能夠響應(yīng)中斷。synchronized的問題是, 持有鎖A后, 如果嘗試獲取鎖B失敗, 那么線程就進(jìn)入阻塞狀態(tài), 一旦發(fā)生死鎖, 就沒有任何機(jī)會來喚醒阻塞的線程。但如果阻塞狀態(tài)的線程能夠響應(yīng)中斷信號, 也就是說當(dāng)我們給阻塞的線程發(fā)送中斷信號的時候, 能夠喚醒它, 那它就有機(jī)會釋放曾經(jīng)持有的鎖A。這樣就破壞了不可剝奪條件了。

(2)支持超時。如果線程在一段時間之內(nèi)沒有獲取到鎖, 不是進(jìn)入阻塞狀態(tài), 而是返回一個錯誤, 那這個線程也有機(jī)會釋放曾經(jīng)持有的鎖。這樣也能破壞不可剝奪條件。

(3)非阻塞地獲取鎖。如果嘗試獲取鎖失敗, 并不進(jìn)入阻塞狀態(tài), 而是直接返回, 那這個線程也有機(jī)會釋放曾經(jīng)持有的鎖。這樣也能破壞不可剝奪條件。

體現(xiàn)在Lock接口上,就是Lock接口提供的三個方法,如下所示。

  1. // 支持中斷的API 
  2. void lockInterruptibly() throws InterruptedException; 
  3. // 支持超時的API 
  4. boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 
  5. // 支持非阻塞獲取鎖的API 
  6. boolean tryLock(); 

lockInterruptibly()

支持中斷。

tryLock()方k

tryLock()方法是有返回值的,它表示用來嘗試獲取鎖,如果獲取成功,則返回true,如果獲取失敗(即鎖已被其他線程獲取),則返回false,也就說這個方法無論如何都會立即返回。在拿不到鎖時不會一直在那等待。

tryLock(long time, TimeUnit unit)方法

tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區(qū)別在于這個方法在拿不到鎖時會等待一定的時間,在時間期限之內(nèi)如果還拿不到鎖,就返回false。如果一開始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true。

也就是說,對于死鎖問題,Lock能夠破壞不可剝奪的條件,例如,我們下面的程序代碼就破壞了死鎖的不可剝奪的條件。

  1. public class TansferAccount{ 
  2.     private Lock thisLock = new ReentrantLock(); 
  3.     private Lock targetLock = new ReentrantLock(); 
  4.     //賬戶的余額 
  5.     private Integer balance; 
  6.     //轉(zhuǎn)賬操作 
  7.     public void transfer(TansferAccount target, Integer transferMoney){ 
  8.         boolean isThisLock = thisLock.tryLock(); 
  9.         if(isThisLock){ 
  10.             try{ 
  11.                 boolean isTargetLock = targetLock.tryLock(); 
  12.                 if(isTargetLock){ 
  13.                     try{ 
  14.                          if(this.balance >= transferMoney){ 
  15.                             this.balance -= transferMoney; 
  16.                             target.balance += transferMoney; 
  17.                         }    
  18.                     }finally{ 
  19.                         targetLock.unlock 
  20.                     } 
  21.                 } 
  22.             }finally{ 
  23.                 thisLock.unlock(); 
  24.             } 
  25.         } 
  26.     } 

例外,Lock下面有一個ReentrantLock,而ReentrantLock支持公平鎖和非公平鎖。

在使用ReentrantLock的時候, ReentrantLock中有兩個構(gòu)造函數(shù), 一個是無參構(gòu)造函數(shù), 一個是傳入fair參數(shù)的構(gòu)造函數(shù)。fair參數(shù)代表的是鎖的公平策略, 如果傳入true就表示需要構(gòu)造一個公平鎖, 反之則表示要構(gòu)造一個非公平鎖。如下代碼片段所示。

  1. //無參構(gòu)造函數(shù): 默認(rèn)非公平鎖 
  2. public ReentrantLock() { 
  3.  sync = new NonfairSync(); 
  4. }  
  5. //根據(jù)公平策略參數(shù)創(chuàng)建鎖 
  6. public ReentrantLock(boolean fair){ 
  7.  sync = fair ? new FairSync() : new NonfairSync(); 

鎖的實(shí)現(xiàn)在本質(zhì)上都對應(yīng)著一個入口等待隊列, 如果一個線程沒有獲得鎖, 就會進(jìn)入等待隊列, 當(dāng)有線程釋放鎖的時候, 就需要從等待隊列中喚醒一個等待的線程。如果是公平鎖, 喚醒的策略就是誰等待的時間長, 就喚醒誰, 很公平;如果是非公平鎖, 則不提供這個公平保證, 有可能等待時間短的線程反而先被喚醒。 而Lock是支持公平鎖的,synchronized不支持公平鎖。

最后,值得注意的是,在使用Lock加鎖時,一定要在finally{}代碼塊中釋放鎖,例如,下面的代碼片段所示。

  1. try{ 
  2.     lock.lock(); 
  3. }finally{ 
  4.     lock.unlock(); 

注:其他synchronized和Lock的詳細(xì)說明,小伙伴們自行查閱即可。

本文轉(zhuǎn)載自微信公眾號「 冰河技術(shù)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系 冰河技術(shù)公眾號。

 

責(zé)任編輯:武曉燕 來源: 冰河技術(shù)
相關(guān)推薦

2009-07-14 14:50:00

JOptionPane

2022-09-29 15:39:10

服務(wù)器NettyReactor

2022-06-07 08:39:35

RPCHTTP

2020-11-25 09:36:17

HTTPRPC遠(yuǎn)程

2019-08-05 14:23:43

DockerKubernetes容器

2024-07-11 10:41:07

HTTPSHTTP文本傳輸協(xié)議

2023-12-11 12:03:14

Python工具元組

2023-01-12 09:01:01

MongoDBMySQL

2020-12-15 06:57:24

java服務(wù)器

2015-09-01 09:28:55

calayeruiview區(qū)別

2015-10-27 09:45:41

2021-03-02 22:10:10

Java互聯(lián)網(wǎng)語言

2021-04-16 23:28:11

Java語言IT

2019-11-20 10:39:35

iPhone緩存清理

2024-04-16 08:26:18

IP地址MAC地址

2021-12-20 10:30:33

forforEach前端

2022-09-13 08:44:02

IP網(wǎng)絡(luò)MAC地址

2014-07-14 15:44:05

虛擬化云計算

2013-10-30 10:47:32

2022-07-12 08:56:18

公平鎖非公平鎖Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號