高并发编程-自定义带有超时功能的锁
文章目錄
- 概述
- 步驟
- 自定義超時(shí)異常處理類
- ILock接口
- 實(shí)現(xiàn)類
- 測試
- 存在的問題
- 修復(fù)存在的問題
- 超時(shí)功能
- 測試超時(shí)功能
- CustomLock
概述
我們知道synchronized的機(jī)制有一個(gè)很重要的特點(diǎn)是:使用synchronized, 當(dāng)一個(gè)線程獲取了鎖,其他線程只能一直等待,等待這個(gè)獲取鎖的線程釋放鎖,如果這個(gè)線程執(zhí)行時(shí)間很長,其他線程就需要一直等待 。 除非獲取鎖的線程執(zhí)行完了該代碼塊,釋放鎖或者線程執(zhí)行發(fā)生異常,JVM會使線程自動釋放鎖。
當(dāng)然了J.U.C包中 Doug Lea大神已經(jīng)設(shè)計(jì)了非常完美的解決方案,我們這里不討論J.U.C的實(shí)現(xiàn)。
我們自己實(shí)現(xiàn)一套的話,該如何實(shí)現(xiàn)呢? 有幾點(diǎn)需要思考
好了,開始吧
步驟
自定義超時(shí)異常處理類
既然要設(shè)計(jì)帶超時(shí)功能的鎖, 少不了當(dāng)超時(shí)時(shí),拋出異常,以便上層捕獲處理。
public class TimeOutException extends RuntimeException {public TimeOutException(String message){super(message);} }ILock接口
約定幾個(gè)接口方法: lock 、lock(long timeout)、unlock、getBlockedThread、getBlockedSize 詳見代碼注釋
package com.artisan.customLock;import java.util.Collection;public interface ILock {/*** 加鎖*/void lock() throws InterruptedException;/*** 加鎖* @param timeout 持有鎖的時(shí)間,過了該時(shí)間(毫秒) 自動釋放該鎖*/void lock(long timeout) throws InterruptedException,TimeOutException;/*** 釋放鎖*/void unlock();/*** 用于觀察 有哪些線程因沒有獲取到鎖被blocked* @return*/Collection<Thread> getBlockedThreads();/*** 被blocked的線程數(shù)量* @return*/int getBlockedSize();}實(shí)現(xiàn)類
詳見代碼注釋。 加鎖和釋放鎖方法 使用 synchronized 修飾,否則使用wait && notifyAll拋出異常
import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Optional;public class CustomLock implements ILock {// 默認(rèn)false// true: 已經(jīng)被線程搶到 false: 空閑private boolean lockFlag;// 用于存儲被blocked的線程,方便查看及計(jì)算被blocked的線程數(shù)量Collection<Thread> blockedThreadCollection = new ArrayList<>();/*** 構(gòu)造函數(shù)中初始化該lockFlag*/public CustomLock(){this.lockFlag = false;}/*** synchronized 修飾該方法* @throws InterruptedException*/@Overridepublic synchronized void lock() throws InterruptedException {// 如果其他線程已經(jīng)獲取到了鎖,讓該線程waitwhile(lockFlag){// 加入到blockedThreadCollectionblockedThreadCollection.add(Thread.currentThread());// waitthis.wait();}// 如果空閑,將該monitor置為trueblockedThreadCollection.remove(Thread.currentThread());lockFlag = true;}@Overridepublic void lock(long timeout) throws InterruptedException, TimeOutException {}@Overridepublic synchronized void unlock() {// 如果是加鎖的線程// 將Monitor置為空閑this.lockFlag = false;Optional.of(Thread.currentThread().getName() + " 釋放lock").ifPresent(System.out::println);// 喚醒其他正在等待的線程this.notifyAll();}@Overridepublic Collection<Thread> getBlockedThreads() {// blockedThreadCollection 可能被其他線程add 或者remove,這里定義為不可變的集合類型return Collections.unmodifiableCollection(blockedThreadCollection);}@Overridepublic int getBlockedSize() {return blockedThreadCollection.size();} }測試
package com.artisan.customLock;import java.time.LocalTime; import java.util.Optional; import java.util.stream.Stream;public class CustomLockTest {public static void main(String[] args) throws InterruptedException {CustomLock customLock = new CustomLock();// 開啟5個(gè)線程Stream.of("T1", "T2", "T3", "T4", "T5").forEach(name -> new Thread(() -> {// 加鎖 處理業(yè)務(wù)try {// 加鎖customLock.lock();Optional.of(Thread.currentThread().getName() + " hold the Monitor").ifPresent(System.out::println);// 調(diào)用業(yè)務(wù)work();} catch (InterruptedException e) {e.printStackTrace();}finally {// 在finally中釋放鎖customLock.unlock();}}, name).start());}/*** 模擬線程的業(yè)務(wù)邏輯** @throws InterruptedException*/public static void work() throws InterruptedException {Optional.of(Thread.currentThread().getName() +" begin to work " + LocalTime.now().withNano(0)).ifPresent(System.out::println);Thread.sleep(3_000);}}日志輸出:
"E:\Program Files\Java\jdk1.8.0_161\bin\java" "-javaagent:E:\Program Files\JetBrains\IntelliJ IDEA 2017.2.4\lib\idea_rt.jar=53159:E:\Program Files\JetBrains\IntelliJ IDEA 2017.2.4\bin" -Dfile.encoding=UTF-8 -classpath "E:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;E:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;D:\IdeaProjects\mvc\target\classes" com.artisan.customLock.CustomLockTest T1 hold the Monitor T1 begin to work 22:19:14 T1 釋放lock T5 hold the Monitor T5 begin to work 22:19:17 T5 釋放lock T2 hold the Monitor T2 begin to work 22:19:20 T2 釋放lock T4 hold the Monitor T4 begin to work 22:19:23 T4 釋放lock T3 hold the Monitor T3 begin to work 22:19:26 T3 釋放lockProcess finished with exit code 0可以看到 確實(shí)是一個(gè)線程拿到鎖后,其他線程必須等待 。
針對第二點(diǎn)呢: 誰加的鎖,必須由誰來釋放 .
我們來測試下
存在的問題
針對第二點(diǎn)呢: 誰加的鎖,必須由誰來釋放 .
我們來測試下 : 假設(shè)我們在main線程中調(diào)用了unlock方法
重新運(yùn)行測試,觀察日志
T1 hold the Monitor T1 begin to work 22:24:41 main 釋放lock T5 hold the Monitor T5 begin to work 22:24:41 T1 釋放lock T2 hold the Monitor T2 begin to work 22:24:44 T5 釋放lock T4 hold the Monitor T4 begin to work 22:24:44 T2 釋放lock T3 hold the Monitor T3 begin to work 22:24:47 T4 釋放lock T3 釋放lockProcess finished with exit code 0T1拿到鎖還沒有工作完,就被主線程釋放了,結(jié)果T5又搶到了… 很明顯不對了 。
修復(fù)存在的問題
見代碼
再次運(yùn)行測試 ,OK
超時(shí)功能
@Overridepublic synchronized void lock(long timeout) throws InterruptedException, TimeOutException {// 入?yún)⒉缓侠?#xff0c;直接調(diào)用lock ,也可拋出異常if (timeout <= 0 ) lock();// 線程等待的剩余時(shí)間long leftTime = timeout;// 計(jì)算結(jié)束時(shí)間long endTime = System.currentTimeMillis() + timeout;while(lockFlag){// 如果超時(shí)了,拋出異常if (leftTime <= 0){throw new TimeOutException(Thread.currentThread().getName() + " 超時(shí)...");}// 加入到blockedThreadCollectionblockedThreadCollection.add(Thread.currentThread());// wait 指定的時(shí)間this.wait(timeout);// 計(jì)算是否超時(shí)leftTime = endTime - System.currentTimeMillis();}// 如果空閑,將該monitor置為trueblockedThreadCollection.remove(Thread.currentThread());this.lockFlag = true;// 將當(dāng)前線程置為lockHolderThreadthis.lockHolderThread = Thread.currentThread();}測試超時(shí)功能
package com.artisan.customLock;import java.time.LocalTime; import java.util.Optional; import java.util.stream.Stream;public class CustomLockTest {public static void main(String[] args) {CustomLock customLock = new CustomLock();// 開啟5個(gè)線程Stream.of("T1", "T2", "T3", "T4", "T5").forEach(name -> new Thread(() -> {// 加鎖 處理業(yè)務(wù)try {// 加鎖 最多等待100毫秒,如果100ms,沒搶到則中斷執(zhí)行customLock.lock(100);Optional.of(Thread.currentThread().getName() + " hold the Monitor").ifPresent(System.out::println);// 調(diào)用業(yè)務(wù)work();} catch (InterruptedException e) {e.printStackTrace();} catch (TimeOutException e){Optional.of(Thread.currentThread().getName() + " timeOut").ifPresent(System.out::println);}finally {// 在finally中釋放鎖customLock.unlock();}}, name).start());}/*** 模擬線程的業(yè)務(wù)邏輯** @throws InterruptedException*/public static void work() throws InterruptedException {Optional.of(Thread.currentThread().getName() +" begin to work " + LocalTime.now().withNano(0)).ifPresent(System.out::println);Thread.sleep(3_000);}}運(yùn)行結(jié)果:
OK。
CustomLock
package com.artisan.customLock;import java.time.LocalTime; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Optional;public class CustomLock implements ILock {// 默認(rèn)false// true: 已經(jīng)被線程搶到 false: 空閑private boolean lockFlag;// 用于存儲被blocked的線程,方便查看及計(jì)算被blocked的線程數(shù)量Collection<Thread> blockedThreadCollection = new ArrayList<>();// 當(dāng)前持有鎖的線程Thread lockHolderThread ;/*** 構(gòu)造函數(shù)中初始化該lockFlag*/public CustomLock(){this.lockFlag = false;}/*** synchronized 修飾該方法* @throws InterruptedException*/@Overridepublic synchronized void lock() throws InterruptedException {// 如果其他線程已經(jīng)獲取到了鎖,讓該線程waitwhile(lockFlag){// 加入到blockedThreadCollectionblockedThreadCollection.add(Thread.currentThread());// waitthis.wait();}// 如果空閑,將該monitor置為trueblockedThreadCollection.remove(Thread.currentThread());this.lockFlag = true;// 將當(dāng)前線程置為lockHolderThreadthis.lockHolderThread = Thread.currentThread();}@Overridepublic synchronized void lock(long timeout) throws InterruptedException, TimeOutException {// 入?yún)⒉缓侠?#xff0c;直接調(diào)用lock ,也可拋出異常if (timeout <= 0 ) lock();// 線程等待的剩余時(shí)間long leftTime = timeout;// 計(jì)算結(jié)束時(shí)間long endTime = System.currentTimeMillis() + timeout;while(lockFlag){// 如果超時(shí)了,拋出異常if (leftTime <= 0){throw new TimeOutException(Thread.currentThread().getName() + " 超時(shí)...");}// 加入到blockedThreadCollectionblockedThreadCollection.add(Thread.currentThread());// wait 指定的時(shí)間this.wait(timeout);// 計(jì)算是否超時(shí)leftTime = endTime - System.currentTimeMillis();}// 如果空閑,將該monitor置為trueblockedThreadCollection.remove(Thread.currentThread());this.lockFlag = true;// 將當(dāng)前線程置為lockHolderThreadthis.lockHolderThread = Thread.currentThread();}@Overridepublic synchronized void unlock() {// 如果是加鎖的線程if(lockHolderThread == Thread.currentThread()){// 將Monitor置為空閑this.lockFlag = false;Optional.of(Thread.currentThread().getName() + " 釋放lock" + LocalTime.now().withNano(0)).ifPresent(System.out::println);// 喚醒其他正在等待的線程this.notifyAll();}}@Overridepublic Collection<Thread> getBlockedThreads() {// blockedThreadCollection 可能被其他線程add 或者remove,這里定義為不可變的集合類型return Collections.unmodifiableCollection(blockedThreadCollection);}@Overridepublic int getBlockedSize() {return blockedThreadCollection.size();} } 《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的高并发编程-自定义带有超时功能的锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高并发编程-线程生产者消费者的综合示例
- 下一篇: 高并发编程-Runtime.getRun