java并發(fā)之Lock接口的深入講解
Juc中各種各樣鎖信息
在java的juc包為我們提供了各種各樣的鎖信息。如果細(xì)心來(lái)看其大部分都會(huì)實(shí)現(xiàn)一個(gè)名為L(zhǎng)OCK的接口信息本文皆在幫你回顧Lock信息;
通過(guò)本文你將了解到如下內(nèi)容:
1.Lock和synchronized的對(duì)比
2.Lock中常見(jiàn)API的總結(jié)
synchronized面臨缺點(diǎn)
鎖的出現(xiàn)主要是為了保證在并發(fā)訪問(wèn)共享資源時(shí)不出現(xiàn)錯(cuò)。 在java中如果不考慮性能損耗問(wèn)題,那么對(duì)共享的資源信息加上synchrionzed關(guān)鍵字基本就可以解決大多數(shù)并發(fā)帶來(lái)的問(wèn)題,但是也隨之帶來(lái)靈活性和效率上的問(wèn)題:
效率方面:
1. 此種情況下鎖的釋放情況較少,很容易到導(dǎo)致一直獨(dú)占資源而導(dǎo)致性能的下降。
2. 當(dāng)我們?cè)噲D獲取鎖時(shí)不能直接指定具體條件
3. 不能中斷正在試圖獲得鎖的線程
靈活性:
1. 當(dāng)獲得鎖資源后,無(wú)法得知是否獲得鎖信息
2. 僅當(dāng)程序異?;蝽樌麍?zhí)行完時(shí)才會(huì)釋放鎖信息,缺乏主動(dòng)釋放鎖的時(shí)機(jī)。
不適用的場(chǎng)景
場(chǎng)景1 :
當(dāng)我們使用synchronized時(shí),假如某線程獲取到鎖之后由于要等待IO或者其他原因進(jìn)入阻塞狀態(tài),同時(shí)未釋放鎖信息,那么此時(shí)其他線程就只能一直等待。所以此時(shí)就需要synchronized有一種機(jī)制:避免等待的線程一直無(wú)期限地等待下去。
場(chǎng)景2 :
在讀文件信息形式,不同線程的寫(xiě)操作是相互沖突的。但是讀操作并不會(huì)導(dǎo)致沖突。如果我們不加考慮的為資源信息加上synchronized關(guān)鍵字,那么當(dāng)多線程同時(shí)操作時(shí),只有一個(gè)線程可以獲取到資源,其他未獲得鎖信息的線程只能進(jìn)入等待狀態(tài),從而導(dǎo)致讀寫(xiě)效率不高。
Lock接口
Lock接口是對(duì)關(guān)鍵字synchronized的補(bǔ)充和擴(kuò)展,它允許我們可以在線程安全的情況下更加靈活的操作共享資源信息。
常見(jiàn)用法:
Lock最佳實(shí)踐:
1.lock(),unlock()
一般來(lái)說(shuō),使用Lock必須在try…catch…塊中進(jìn)行,并且將釋放鎖的操作放在finally塊中進(jìn)行。這是因?yàn)閘ock并不會(huì)像synchronized那樣在異常時(shí)釋放鎖,所以必須保證有手動(dòng)釋放的過(guò)程,這樣才能保證其它線程有獲取鎖的機(jī)會(huì)。
// 加鎖 lock.lock(); try{ //處理任務(wù) }catch(Exception ex){ }finally{ //釋放鎖 (鎖的釋放一般放入到finally塊中進(jìn)行,這樣保證了總會(huì)對(duì)鎖信息進(jìn)行釋放) lock.unlock(); }
2. tryLock() & tryLock(long time, TimeUnit unit)
tryLock()方法是有返回值的,它表示用來(lái)嘗試獲取鎖,如果獲取成功,則返回true;如果獲取失?。存i已被其他線程獲取),則返回false,也就是說(shuō),這個(gè)方法無(wú)論如何都會(huì)立即返回即使其無(wú)法獲取到鎖也不會(huì)一致等待。
tryLock(long time, TimeUnit unit)方法和tryLock()方法是類(lèi)似的,只不過(guò)區(qū)別在于這個(gè)方法在拿不到鎖時(shí)會(huì)等待一定的時(shí)間,在時(shí)間期限之內(nèi)如果還拿不到鎖,就返回false
如果一開(kāi)始拿到鎖或者在等待期間內(nèi)拿到了鎖,則返回true。一般情況下,通過(guò)tryLock來(lái)獲取鎖時(shí)是這樣使用的:
Lock lock = ...; if(lock.tryLock()) { try{ //處理任務(wù) }catch(Exception ex){ }finally{ lock.unlock(); //釋放鎖 } }else { //如果不能獲取鎖,則直接做其他事情 }
3. lockInterruptibly()
lockInterruptibly()方法比較特殊,當(dāng)通過(guò)這個(gè)方法去獲取鎖時(shí),如果線程 正在等待獲取鎖,則這個(gè)線程能夠 響應(yīng)中斷,即中斷線程的等待狀態(tài)。
例如,當(dāng)兩個(gè)線程同時(shí)通過(guò)lock.lockInterruptibly()想獲取某個(gè)鎖時(shí),假若此時(shí)線程A獲取到了鎖,而線程B只有在等待,那么對(duì)線程B調(diào)用threadB.interrupt()方法能夠中斷線程B的等待過(guò)程。
public void method() throws InterruptedException { lock.lockInterruptibly(); try { //..... } finally { lock.unlock(); } }
當(dāng)一個(gè)線程獲取了鎖之后,是不會(huì)被interrupt()方法中斷的。因?yàn)閕nterrupt()方法只能中斷阻塞過(guò)程中的線程而不能中斷正在運(yùn)行過(guò)程中的線程。而在 synchronized 中,當(dāng)一個(gè)線程處于等待某個(gè)鎖的狀態(tài),是無(wú)法被中斷的,只有一直等待下去,這也就是我們需要手動(dòng)釋放鎖的原因。
給出如下的例子來(lái)進(jìn)行驗(yàn)證:創(chuàng)建兩個(gè)線程來(lái)共同爭(zhēng)搶lock鎖信息
public class LockInterruptibly implements Runnable { private Lock lock = new ReentrantLock(); public static void main(String[] args) { LockInterruptibly lockInterruptibly = new LockInterruptibly(); Thread thread0 = new Thread(lockInterruptibly); Thread thread1 = new Thread(lockInterruptibly); thread0.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } thread1.start(); thread1.interrupt(); } // 任務(wù)執(zhí)行邏輯 @Override public void run() { System.out.println(Thread.currentThread().getName() + "嘗試獲取鎖"); try { lock.lockInterruptibly(); try { System.out.println(Thread.currentThread().getName() + "獲取到了鎖"); Thread.sleep(5000); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "睡眠期間被中斷了"); } finally { lock.unlock(); System.out.println(Thread.currentThread().getName() + "釋放了鎖"); } } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "獲得鎖期間被中斷了"); } } }
執(zhí)行結(jié)果:
Thread-0嘗試獲取鎖
Thread-0獲取到了鎖
Thread-1嘗試獲取鎖
Thread-1獲得鎖期間被中斷了
Thread-0釋放了鎖
通過(guò)結(jié)果信息我們可以看出,lockInterruptibly()僅能中斷正在等待的線程信息,而無(wú)法中斷正在運(yùn)行的線程。
對(duì)比 Lock和tryLock的區(qū)別
lock和tryLock都可以獲取到鎖信息,但兩者之間還是存在些差異的。 具體如下:
1: lock拿不到鎖會(huì)一直等待。tryLock是去嘗試,拿不到就返回false,拿到返回true。
2: tryLock是可以被打斷的,被中斷的,lock是不可以。
// 實(shí)例代碼 public class LockDemo implements Runnable{ static Lock lock1 = new ReentrantLock(); @Override public void run() { // 分別演示 lock,trylock區(qū)別 // lock1.lock(); lock1.tryLock(); System.out.println("線程 " + Thread.currentThread().getName() + " 獲取到鎖信息 "); } public static void main(String[] args) throws InterruptedException { LockDemo r1 = new LockDemo(); LockDemo r2 = new LockDemo(); r1.flag = true; r2.flag = false; Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); Thread.sleep(1000); // 中斷 t2.start(); t2.interrupt(); } }
結(jié)果信息:
當(dāng)執(zhí)行l(wèi)ock1.lock()時(shí)的輸出:可以看到lock方法并不能響應(yīng)中斷信息,如果不解鎖則會(huì)一致持有鎖信息!
對(duì)于tryLock而言其可以響應(yīng)中斷
總結(jié)
本篇對(duì)Lock接口中常用到的Api進(jìn)行了分析和總結(jié),同時(shí)分析了Lock接口和synchronized關(guān)鍵之間的關(guān)系,希望對(duì)你能有所啟發(fā).
到此這篇關(guān)于java并發(fā)之Lock接口的文章就介紹到這了,更多相關(guān)java并發(fā)Lock接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Netty分布式ByteBuf使用的底層實(shí)現(xiàn)方式源碼解析
這篇文章主要為大家介紹了Netty分布式ByteBuf使用底層實(shí)現(xiàn)方式源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03java字符串轉(zhuǎn)數(shù)字及各種數(shù)字轉(zhuǎn)字符串的3種方法
這篇文章主要介紹了java字符串轉(zhuǎn)數(shù)字及各種數(shù)字轉(zhuǎn)字符串的3種方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-09-09如何使用Spring Cloud Feign日志查看請(qǐng)求響應(yīng)
這篇文章主要介紹了如何使用Spring Cloud Feign日志查看請(qǐng)求響應(yīng),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02System.getProperty(“l(fā)ine.separator“)含義及意義詳解
這篇文章主要介紹了System.getProperty(“l(fā)ine.separator“)含義,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05妙用Java8中的Function接口消滅if...else
在開(kāi)發(fā)過(guò)程中經(jīng)常會(huì)使用if...else...進(jìn)行判斷拋出異常、分支處理等操作。這些if...else...充斥在代碼中嚴(yán)重影響了代碼代碼的美觀,本文就妙用Java8中的Function接口消滅if...else,感興趣的可以了解一下2022-01-01SpringBoot?mybatis-plus使用json字段實(shí)戰(zhàn)指南
在現(xiàn)代應(yīng)用開(kāi)發(fā)中經(jīng)常會(huì)使用JSON格式存儲(chǔ)和傳輸數(shù)據(jù),為了便捷地處理數(shù)據(jù)庫(kù)中的JSON字段,MyBatis-Plus提供了強(qiáng)大的JSON處理器,這篇文章主要給大家介紹了關(guān)于SpringBoot?mybatis-plus使用json字段的相關(guān)資料,需要的朋友可以參考下2024-01-01java截取字符串中的指定字符的兩種方法(以base64圖片為例)
本文介紹了使用Java截取字符串中指定字符的方法,通過(guò)substring索引和正則實(shí)現(xiàn),文章詳細(xì)介紹了實(shí)現(xiàn)步驟和示例代碼,對(duì)于想要了解如何使用Java截取字符串指定字符的讀者具有一定的參考價(jià)值2023-08-08