Java并發(fā)Lock接口實(shí)現(xiàn)示例詳解
Locks包 類層次結(jié)構(gòu)
Lock接口
方法簽名 | 描述 | 說(shuō)明 |
---|---|---|
void lock(); | 獲取鎖(不死不休) | 一直獲取鎖,直到拿到為止 |
boolean tryLock(); | 獲取鎖(淺嘗輒止) | 嘗試獲得鎖,獲取不到就算了 |
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; | 獲取鎖(過(guò)時(shí)不候) | 超時(shí)限制,超過(guò)時(shí)間就放棄 |
void lockInterruptibly() throws InterruptedException; | 獲取鎖(任人擺布) | 可以在外部通過(guò)方法中斷 |
void unlock(); | 釋放鎖 | |
Condition newCondition(); |
結(jié)論:
1、lock()最常用;
2、lockInterruptibly()方法一般更昂貴,有的impl可能沒(méi)有實(shí)現(xiàn)lockInterruptibly(),只有真的需要效應(yīng)中斷時(shí),才使用,使用之前看看impl對(duì)該方法的描述。
trylock
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主線程拿到鎖 Thread th = new Thread(new Runnable() { @Override public void run() { boolean rs = lock.tryLock(); System.out.println("是否獲取到鎖: " + rs); } }); th.start(); Thread.sleep(2000L); th.interrupt();//中斷線程運(yùn)行 System.out.println("th 線程被中斷了"); } }
是否獲取到鎖: false
th 線程被中斷了
- trylock帶超時(shí)
package lock; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主線程拿到鎖 Thread th = new Thread(new Runnable() { @Override public void run() { boolean rs = false; try { rs = lock.tryLock(1, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("是否獲取到鎖: " + rs); } }); th.start(); Thread.sleep(2000L); th.interrupt();//中斷線程運(yùn)行 System.out.println("th 線程被中斷了"); } }
是否獲取到鎖: false
th 線程被中斷了
lockInterruptibly
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主線程拿到鎖 Thread th = new Thread(new Runnable() { @Override public void run() { try { lock.lockInterruptibly(); } catch (InterruptedException e) { System.out.println("獲取鎖時(shí)被中斷了"); e.printStackTrace(); } } }); th.start(); Thread.sleep(2000L); th.interrupt();//中斷線程運(yùn)行 System.out.println("th 線程被中斷了"); } }
th 線程被中斷了
獲取鎖時(shí)被中斷了
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at lock.GetLock_Demo$1.run(GetLock_Demo.java:16)
at java.lang.Thread.run(Thread.java:748)
lock and unlock
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class GetLock_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) throws InterruptedException { lock.lock(); //主線程拿到鎖 Thread th = new Thread(new Runnable() { @Override public void run() { System.out.println("嘗試獲得鎖"); lock.lock(); System.out.println("獲得鎖了"); } }); th.start(); Thread.sleep(2000L); th.interrupt();//中斷線程運(yùn)行 System.out.println("th 線程被中斷了"); Thread.sleep(5000L); lock.unlock(); } }
嘗試獲得鎖
th 線程被中斷了
獲得鎖了
Condition
Condition 一般是將其中的await和signal成對(duì)使用的,且一般是await在前signal在后,而且調(diào)用的使用,應(yīng)該確保本身是獲取到鎖的情況下,不然會(huì)出現(xiàn)以下問(wèn)題:
1. await 和 signal 方法應(yīng)該在lock內(nèi)部調(diào)用,否則會(huì)發(fā)生 IllegalMonitorStateException
異常
package lock; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Condition_Demo { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { lock.lock(); try { System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "獲得鎖"); condition.await(); //因?yàn)檫@里將線程掛起,所以后面無(wú)法執(zhí)行 System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "開(kāi)始執(zhí)行~"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }; thread.start(); Thread.sleep(2000L); System.out.println("休眠2s,來(lái)控制線程"); condition.signal(); //直接喚醒會(huì)報(bào)錯(cuò),因?yàn)閘ock方法執(zhí)行在Thread-0線程內(nèi)部,而我們代碼在這里執(zhí)行的是main線程,所以會(huì)報(bào)錯(cuò), } }
當(dāng)前線程:Thread-0獲得鎖
休眠2s,來(lái)控制線程
Exception in thread "main" java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
at lock.Condition_Demo.main(Condition_Demo.java:33)
2. signal應(yīng)該在await后調(diào)用,否則會(huì)導(dǎo)致死鎖
package lock; import sync.ReentrantLockDemo; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Condition_Demo { private static Lock lock = new ReentrantLock(); private static Condition condition = lock.newCondition(); public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { try { Thread.sleep(3000L); System.out.println("休眠3秒,等待主線程先執(zhí)行."); } catch (InterruptedException e) { e.printStackTrace(); } lock.lock(); try { System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "獲得鎖"); condition.await(); //因?yàn)檫@里將線程掛起,所以后面無(wú)法執(zhí)行 System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + "開(kāi)始執(zhí)行~"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }; thread.start(); Thread.sleep(2000L); System.out.println("休眠2s,來(lái)控制線程"); lock.lock(); condition.signal(); //直接喚醒會(huì)報(bào)錯(cuò),因?yàn)閘ock方法執(zhí)行在Thread-0線程內(nèi)部,而我們代碼在這里執(zhí)行的是main線程,所以會(huì)報(bào)錯(cuò), lock.unlock(); //獲取到了這把鎖,然后解鎖. //2.當(dāng)然這里會(huì)出現(xiàn)死鎖的,如果signal方法在我們的await之前執(zhí)行,那么這里就會(huì)死鎖 } }
休眠2s,來(lái)控制線程
休眠3秒,等待主線程先執(zhí)行.
當(dāng)前線程:Thread-0獲得鎖
// 這里死鎖了
- 使用condition實(shí)現(xiàn)阻塞隊(duì)列的例子
package lock; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class BlockingQueue_Demo { public static void main(String[] args) throws InterruptedException { BlockingQueue kaneBlockingQueue = new BlockingQueue(6); new Thread() { @Override public void run() { for (int i = 0; i < 10; i++) { kaneBlockingQueue.put("x" + i); } } }.start(); Thread.sleep(1000L); System.out.println("開(kāi)始取元素"); for (int i = 0; i < 8; i++) { kaneBlockingQueue.take(); Thread.sleep(2000); } } } class BlockingQueue { List<Object> list = new ArrayList<>(); private Lock lock = new ReentrantLock(); private Condition putCondition = lock.newCondition(); //condition可以有多個(gè),針對(duì)不同的操作放入不同condition,相當(dāng)于等待隊(duì)列 private Condition takeCondition = lock.newCondition(); private int length; public BlockingQueue(int length) { this.length = length; } public void put(Object obj) { lock.lock(); //思考一個(gè)讀一個(gè)寫,為什么要加鎖呢? try { while (true) { if (list.size() < length) { //我們集合的長(zhǎng)度不能超過(guò)規(guī)定的長(zhǎng)度,才能向里面放東西 list.add(obj); System.out.println("隊(duì)列中放入元素:" + obj); takeCondition.signal(); return; } else { //如果放不進(jìn)去,就該阻塞. --利用condition實(shí)現(xiàn) putCondition.await();//掛起 } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public Object take() { lock.lock(); try { while (true) { if (list.size() > 0) { Object obj = list.remove(0); System.out.println("隊(duì)列中取得元素:" + obj); putCondition.signal(); return obj; } else { takeCondition.await(); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); return null; } } }
隊(duì)列中放入元素:x0
隊(duì)列中放入元素:x1
隊(duì)列中放入元素:x2
隊(duì)列中放入元素:x3
隊(duì)列中放入元素:x4
隊(duì)列中放入元素:x5
開(kāi)始取元素
隊(duì)列中取得元素:x0
隊(duì)列中放入元素:x6
隊(duì)列中取得元素:x1
隊(duì)列中放入元素:x7
隊(duì)列中取得元素:x2
隊(duì)列中放入元素:x8
隊(duì)列中取得元素:x3
隊(duì)列中放入元素:x9
隊(duì)列中取得元素:x4
隊(duì)列中取得元素:x5
隊(duì)列中取得元素:x6
隊(duì)列中取得元素:x7
Process finished with exit code 0
可重入鎖 ReentrantLock
一般來(lái)說(shuō),如果可重入鎖的加鎖次數(shù)是n,那么解鎖次數(shù)也得是n才能完全釋放鎖,否則,如果小于n 則無(wú)法正常釋放鎖,此時(shí)如果有別的線程要加鎖,則無(wú)法獲取到鎖而被阻塞;如果大于n,則會(huì)觸發(fā) IllegalMonitorStateException
異常, ReentrantLock 默認(rèn)是使用非公平鎖,如果要使用公平鎖,可以使用 new ReentrantLock(true)
來(lái)創(chuàng)建。
- 小于n的情況
package lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Reentrant_Demo { static Lock lock = new ReentrantLock(); public static void main(String[] args) { lock.lock(); System.out.println(Thread.currentThread().getName() + "獲得第1次鎖"); lock.lock(); System.out.println(Thread.currentThread().getName() + "獲得第2次鎖"); lock.unlock(); new Thread() { @Override public void run() { System.out.println(Thread.currentThread().getName() + "開(kāi)始去釋放鎖"); lock.lock(); System.out.println("獲得鎖成功~~~"); lock.unlock(); } }.start(); } }
main獲得第1次鎖 main獲得第2次鎖 Thread-0開(kāi)始去釋放鎖 // 子線程獲取鎖失敗導(dǎo)致阻塞了
- 大于n的情況修改成3次unlock
// 修改成3次unlock lock.unlock(); lock.unlock(); lock.unlock();
main獲得第1次鎖 main獲得第2次鎖 Exception in thread "main" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151) at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) at lock.Reentrant_Demo.main(Reentrant_Demo.java:18)
簡(jiǎn)單說(shuō)明圖
實(shí)現(xiàn)一個(gè)ReenrantLock的demo版本 - 一個(gè)現(xiàn)實(shí)思想的簡(jiǎn)單版本
package lock; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.LockSupport; public class ReentrantLock_Demo implements Lock { //記錄鎖的擁有者 AtomicReference<Thread> owner = new AtomicReference<>(); //記錄重入次數(shù)的count AtomicInteger count = new AtomicInteger(0); //等待隊(duì)列 private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue(); @Override public boolean tryLock() { //判斷count值是否為0,如果count不等于0,說(shuō)明鎖被占用 int ct = count.get(); //判斷鎖是不是自己占用的,做重入 if (ct != 0) { if (Thread.currentThread() == owner.get()) { count.set(ct + 1); return true; } } else { //若count為0 ,表示當(dāng)前鎖未被占用,通過(guò)CAS操作 if (count.compareAndSet(ct, ct + 1)) { owner.set(Thread.currentThread()); //如果不是自己,進(jìn)入隊(duì)列 return true; } } return false; } @Override public void lock() { if (!tryLock()) { //加入等待隊(duì)列 waiters.offer(Thread.currentThread()); while (true) { //若線程是隊(duì)列頭部,先判斷一次,現(xiàn)在能不能去搶,然后再去加鎖 Thread head = waiters.peek(); if (head == Thread.currentThread()) { if (!tryLock()) { LockSupport.park(); } else { waiters.poll(); return; } } else { LockSupport.park(); } } } } public boolean tryUnlock() { if (owner.get() != Thread.currentThread()) { throw new IllegalMonitorStateException(); } else { int ct = count.get(); int nextc = ct - 1; count.set(nextc); if (nextc == 0) { //可重入鎖被加鎖多次,一旦為0 就釋放鎖,如果不是0,還得繼續(xù)釋放 owner.compareAndSet(Thread.currentThread(), null); return true; } else { return false; } } } @Override public void unlock() { if (tryUnlock()) { Thread head = waiters.peek(); if (head != null) { LockSupport.unpark(head); } } } /** * 暫時(shí)忽略 * * @throws InterruptedException */ @Override public void lockInterruptibly() throws InterruptedException { } /** * 暫時(shí)忽略 * * @throws InterruptedException */ @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return false; } /** * 暫時(shí)忽略 * * @throws InterruptedException */ @Override public Condition newCondition() { return null; } }
以上就是Java并發(fā)Lock接口實(shí)現(xiàn)示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java并發(fā)Lock接口的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- linux shell實(shí)現(xiàn)隨機(jī)數(shù)幾種方法分享(date,random,uuid)
- PowerShell中使用正則和ValidateSet驗(yàn)證參數(shù)合法性
- PowerShell中iso8601格式日期和DateTime對(duì)象互轉(zhuǎn)實(shí)例
- Shell腳本實(shí)現(xiàn)隨機(jī)數(shù)多種方法介紹(date、random、uuid)
- java并發(fā)之Lock接口的深入講解
- Java @GlobalLock注解詳細(xì)分析講解
- 一文了解Java讀寫鎖ReentrantReadWriteLock的使用
- 詳解Java?ReentrantLock可重入,可打斷,鎖超時(shí)的實(shí)現(xiàn)原理
相關(guān)文章
AsyncHttpClient的TimeoutTimerTask連接池異步超時(shí)
這篇文章主要為大家介紹了AsyncHttpClient的TimeoutTimerTask連接池異步超時(shí)源碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12Java中String類的一些常見(jiàn)方法總結(jié)
這篇文章主要給大家介紹了關(guān)于Java中String類的一些常見(jiàn)方法,文中包括了Java中String類的基本概念、構(gòu)造方式、常用方法以及StringBuilder和StringBuffer的使用,涵蓋了字符串操作的各個(gè)方面,包括查找、轉(zhuǎn)換、比較、替換、拆分、截取等,需要的朋友可以參考下2024-11-11SpringSecurity安全框架在SpringBoot框架中的使用詳解
在Spring?Boot框架中,Spring?Security是一個(gè)非常重要的組件,它可以幫助我們實(shí)現(xiàn)應(yīng)用程序的安全性,本文將詳細(xì)介紹Spring?Security在Spring?Boot框架中的使用,包括如何配置Spring?Security、如何實(shí)現(xiàn)身份驗(yàn)證和授權(quán)、如何防止攻擊等2023-06-06Java實(shí)現(xiàn)支付寶之第三方支付寶即時(shí)到賬支付功能
這篇文章主要介紹了Java實(shí)現(xiàn)支付寶之第三方支付寶即時(shí)到賬支付功能的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07SpringBoot實(shí)現(xiàn)對(duì)Http接口進(jìn)行監(jiān)控的代碼
Spring Boot Actuator是Spring Boot提供的一個(gè)模塊,用于監(jiān)控和管理Spring Boot應(yīng)用程序的運(yùn)行時(shí)信息,本文將介紹一下Spring Boot Actuator以及代碼示例,以及如何進(jìn)行接口請(qǐng)求監(jiān)控,需要的朋友可以參考下2024-07-07Java如何獲取JSONObject內(nèi)指定字段key的value值
這篇文章主要介紹了Java如何獲取JSONObject內(nèi)指定字段key的value值問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12基于spring boot實(shí)現(xiàn)一個(gè)全局異常處理器
在項(xiàng)目開(kāi)發(fā)中,我們可以基于spring boot提供的切面特性,來(lái)很輕松的實(shí)現(xiàn)全局異常的處理,所以本文主要為大家介紹了如何基于spring boot實(shí)現(xiàn)一個(gè)全局異常處理器,有需要的可以參考下2023-09-09使用Java將字節(jié)數(shù)組轉(zhuǎn)成16進(jìn)制形式的代碼實(shí)現(xiàn)
在很多場(chǎng)景下,需要進(jìn)行分析字節(jié)數(shù)據(jù),但是我們存起來(lái)的字節(jié)數(shù)據(jù)一般都是二進(jìn)制的,這時(shí)候就需要我們將其轉(zhuǎn)成16進(jìn)制的方式方便分析,本文主要介紹如何使用Java將字節(jié)數(shù)組格式化成16進(jìn)制的格式并輸出,需要的朋友可以參考下2024-05-05java實(shí)現(xiàn)mysql操作類分享 java連接mysql
這篇文章主要介紹了java實(shí)現(xiàn)的mysql操作類示例,大家在連接數(shù)據(jù)的時(shí)候可以直接使用了2014-01-01