文章目录

  • 概述
  • 步骤
    • 自定义超时异常处理类
    • ILock接口
    • 实现类
    • 测试
    • 存在的问题
    • 修复存在的问题
    • 超时功能
    • 测试超时功能
  • CustomLock

概述

我们知道synchronized的机制有一个很重要的特点是:使用synchronized, 当一个线程获取了锁,其他线程只能一直等待,等待这个获取锁的线程释放锁,如果这个线程执行时间很长,其他线程就需要一直等待 。 除非获取锁的线程执行完了该代码块,释放锁或者线程执行发生异常,JVM会使线程自动释放锁。

当然了J.U.C包中 Doug Lea大神已经设计了非常完美的解决方案,我们这里不讨论J.U.C的实现。

我们自己实现一套的话,该如何实现呢? 有几点需要思考

  1. 原有的synchronized功能,必须保证,即一个线程拿到锁后,其他线程必须等待
  2. 谁加的锁,必须由谁来释放
  3. 加入超时功能

好了,开始吧


步骤

自定义超时异常处理类

既然要设计带超时功能的锁, 少不了当超时时,抛出异常,以便上层捕获处理。

public class TimeOutException extends  RuntimeException {public TimeOutException(String message){super(message);}
}

ILock接口

约定几个接口方法: lock 、lock(long timeout)、unlock、getBlockedThread、getBlockedSize 详见代码注释

package com.artisan.customLock;import java.util.Collection;public interface ILock {/*** 加锁*/void lock() throws InterruptedException;/*** 加锁* @param timeout 持有锁的时间,过了该时间(毫秒) 自动释放该锁*/void lock(long timeout) throws InterruptedException,TimeOutException;/*** 释放锁*/void unlock();/*** 用于观察 有哪些线程因没有获取到锁被blocked* @return*/Collection<Thread> getBlockedThreads();/*** 被blocked的线程数量* @return*/int getBlockedSize();}

实现类

详见代码注释。 加锁和释放锁方法 使用 synchronized 修饰,否则使用wait && notifyAll抛出异常


import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;public class CustomLock implements ILock {// 默认false// true: 已经被线程抢到  false: 空闲private boolean lockFlag;// 用于存储被blocked的线程,方便查看及计算被blocked的线程数量Collection<Thread> blockedThreadCollection = new ArrayList<>();/*** 构造函数中初始化该lockFlag*/public CustomLock(){this.lockFlag = false;}/*** synchronized 修饰该方法* @throws InterruptedException*/@Overridepublic synchronized void lock() throws InterruptedException {// 如果其他线程已经获取到了锁,让该线程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个线程Stream.of("T1", "T2", "T3", "T4", "T5").forEach(name -> new Thread(() -> {// 加锁 处理业务try {// 加锁customLock.lock();Optional.of(Thread.currentThread().getName() + " hold the Monitor").ifPresent(System.out::println);// 调用业务work();} catch (InterruptedException e) {e.printStackTrace();}finally {// 在finally中释放锁customLock.unlock();}}, name).start());}/*** 模拟线程的业务逻辑** @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

可以看到 确实是一个线程拿到锁后,其他线程必须等待 。

针对第二点呢: 谁加的锁,必须由谁来释放 .

我们来测试下

存在的问题

针对第二点呢: 谁加的锁,必须由谁来释放 .

我们来测试下 : 假设我们在main线程中调用了unlock方法

重新运行测试,观察日志

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 0

T1拿到锁还没有工作完,就被主线程释放了,结果T5又抢到了… 很明显不对了 。

修复存在的问题

见代码

再次运行测试 ,OK


超时功能

 @Overridepublic synchronized  void lock(long timeout) throws InterruptedException, TimeOutException {// 入参不合理,直接调用lock ,也可抛出异常if (timeout <= 0 ) lock();// 线程等待的剩余时间long  leftTime = timeout;// 计算结束时间long endTime = System.currentTimeMillis() + timeout;while(lockFlag){// 如果超时了,抛出异常if (leftTime <= 0){throw new TimeOutException(Thread.currentThread().getName() + " 超时...");}// 加入到blockedThreadCollectionblockedThreadCollection.add(Thread.currentThread());// wait 指定的时间this.wait(timeout);// 计算是否超时leftTime = endTime - System.currentTimeMillis();}// 如果空闲,将该monitor置为trueblockedThreadCollection.remove(Thread.currentThread());this.lockFlag = true;// 将当前线程置为lockHolderThreadthis.lockHolderThread = Thread.currentThread();}

测试超时功能

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个线程Stream.of("T1", "T2", "T3", "T4", "T5").forEach(name -> new Thread(() -> {// 加锁 处理业务try {// 加锁 最多等待100毫秒,如果100ms,没抢到则中断执行customLock.lock(100);Optional.of(Thread.currentThread().getName() + " hold the Monitor").ifPresent(System.out::println);// 调用业务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());}/*** 模拟线程的业务逻辑** @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);}}

运行结果:

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 {// 默认false// true: 已经被线程抢到  false: 空闲private boolean lockFlag;// 用于存储被blocked的线程,方便查看及计算被blocked的线程数量Collection<Thread> blockedThreadCollection = new ArrayList<>();// 当前持有锁的线程Thread lockHolderThread ;/*** 构造函数中初始化该lockFlag*/public CustomLock(){this.lockFlag = false;}/*** synchronized 修饰该方法* @throws InterruptedException*/@Overridepublic synchronized void lock() throws InterruptedException {// 如果其他线程已经获取到了锁,让该线程waitwhile(lockFlag){// 加入到blockedThreadCollectionblockedThreadCollection.add(Thread.currentThread());// waitthis.wait();}// 如果空闲,将该monitor置为trueblockedThreadCollection.remove(Thread.currentThread());this.lockFlag = true;// 将当前线程置为lockHolderThreadthis.lockHolderThread = Thread.currentThread();}@Overridepublic synchronized  void lock(long timeout) throws InterruptedException, TimeOutException {// 入参不合理,直接调用lock ,也可抛出异常if (timeout <= 0 ) lock();// 线程等待的剩余时间long  leftTime = timeout;// 计算结束时间long endTime = System.currentTimeMillis() + timeout;while(lockFlag){// 如果超时了,抛出异常if (leftTime <= 0){throw new TimeOutException(Thread.currentThread().getName() + " 超时...");}// 加入到blockedThreadCollectionblockedThreadCollection.add(Thread.currentThread());// wait 指定的时间this.wait(timeout);// 计算是否超时leftTime = endTime - System.currentTimeMillis();}// 如果空闲,将该monitor置为trueblockedThreadCollection.remove(Thread.currentThread());this.lockFlag = true;// 将当前线程置为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();}
}

高并发编程-自定义带有超时功能的锁相关推荐

  1. 高并发编程-自定义简易的线程池(2),体会原理

    文章目录 概述 示例 概述 高并发编程-自定义简易的线程池(1),体会原理 中只实现了任务队列,我们这里把其余的几个也补充进来 拒绝策略 关闭线程池 最小 最大 活动线程数 - 示例 比较简单,直接上 ...

  2. 高并发编程-自定义简易的线程池(1),体会原理

    文章目录 概述 示例 概述 我们工作中,并发编程必然离不开jdk提供的j.u.c中的线程池 ,假设让我们自己去设计一个线程池,该从哪几个方面来着手考虑呢? 首先: 既然是线程池 , 那必然 有个初始化 ...

  3. Java 面试知识点解析(二)——高并发编程篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  4. libevent c++高并发网络编程_高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...

  5. 高并发编程学习(2)——线程通信详解

    前序文章 高并发编程学习(1)--并发基础 - https://www.wmyskxz.com/2019/11/26/gao-bing-fa-bian-cheng-xue-xi-1-bing-fa-j ...

  6. Java高并发编程详解系列-Java线程入门

    根据自己学的知识加上从各个网站上收集的资料分享一下关于java高并发编程的知识点.对于代码示例会以Maven工程的形式分享到个人的GitHub上面.   首先介绍一下这个系列的东西是什么,这个系列自己 ...

  7. Java高并发编程 (马士兵老师视频)笔记(一)同步器

    本篇主要总结同步器的相关例子:包括synchronized.volatile.原子变量类(AtomicXxx).CountDownLatch.ReentrantLock和ThreadLocal.还涉及 ...

  8. java线程高并发编程

    java线程详解及高并发编程庖丁解牛 线程概述: 祖宗: 说起java高并发编程,就不得不提起一位老先生Doug Lea,这位老先生可不得了,看看百度百科对他的评价,一点也不为过: 如果IT的历史,是 ...

  9. Java JUC高并发编程(一)

    目录 一.概述 二.Lock接口 三.线程间的通信 解决虚假唤醒问题 Lock通信示例: 四.线程间定制化通信 一.概述 JUC就是java.util.concurrent工具包的简称,这是一个处理线 ...

最新文章

  1. 人类首次登月50年后,这60家公司决心重返月球
  2. 【嵌入式开发】 Bootloader 详解 ( 代码环境 | ARM 启动流程 | uboot 工作流程 | 架构设计)
  3. 【知识星球】每日干货看图猜技术,你都会吗?
  4. Matlab 基于svm的图像物体分类
  5. go mysql 查询语句_01 MySQL-初识MySQL-查询语句的执行流程-Go语言中文社区
  6. 第一次ActiveX Fuzzing测试
  7. DL中常用的numpy
  8. MySQL学习(4)多表查询
  9. jvm延迟偏向_用于偏向硬币翻转模拟的Python程序
  10. Ubuntu 16.04 火狐添加java插件、解决Firefox强制低版本java插件运行以及安全设置限制自签名应用程序运行
  11. 机器人任务规划:从状态机到形式系统
  12. C++友元函数简单示例
  13. 【信号与系统|吴大正】1:信号与系统概述
  14. python量化交易通达信_分享一个可以实战的量化交易策略(适用于通达信系统)...
  15. python截图搜题_用python的OCR实现自动截图搜题
  16. 一篇述说“山寨”的文章,转过来大家看看。
  17. week15 作业哈希算法
  18. 每周分享第 10 期
  19. vscode插件不兼容无法安装
  20. rip路由的 负载分担

热门文章

  1. 如何将切换anaconda 的Python 版本
  2. pandas Series DataFrame 丢弃指定轴上的项(三)
  3. 会议论文影响因子多少_中国学术期刊影响因子年报(2020版)发布|中国心血管杂志影响力指数3年连升...
  4. 论文笔记: Modeling Extreme Events in Time Series Prediction
  5. 推荐系统笔记:使用分类模型进行协同过滤
  6. DQN 笔记 State-action Value Function(Q-function)
  7. 文巾解题 557. 反转字符串中的单词 III
  8. 机器学习中的矩阵向量求导(四) 矩阵向量求导链式法则
  9. shell编程 case语句
  10. QT利用lamda正则表达式取出字符串中的浮点数与整数