当前位置: 首页 > news >正文

多线程学习篇七:ReentrantLock

1. ReentrantLock 特征

  • 可重入
  • 可打断
  • 可尝试(在指定时间段)获取锁
  • 可设置公平(非公平)锁
  • 支持多个条件变量

2. 案例演示

2.1 可重入

@Slf4j(topic = "c.Test01")
public class Test01 {private static final ReentrantLock LOCK = new ReentrantLock();public static void m1() {LOCK.lock();try {log.info("execute m1");m2();} finally {LOCK.unlock();}}public static void m2() {LOCK.lock();try {log.info("execute m2");m3();} finally {LOCK.unlock();}}public static void m3() {LOCK.lock();try {log.info("execute m3");} finally {LOCK.unlock();}}public static void main(String[] args) {m1();}
}

通过运行结果得出结论:main 线程在执行 m1 方法的时候获取到锁,在执行 m2、m3 方法时同样获取到了锁,所以 ReentrantLock 是可重入的

2.2 可打断

2.2.1 使用 lockInterruptibly 的方式获取锁
@Slf4j(topic = "c.Test02")
public class Test02 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {try {log.info("lock interruptibly");lock.lockInterruptibly();} catch (InterruptedException e) {log.info(Thread.currentThread().getName() + "线程被打断");return;}try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "t1");lock.lock();log.info(Thread.currentThread().getName() + " 获取到锁");t1.start();try {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}log.info(Thread.currentThread().getName() + " 执行打断");t1.interrupt();} finally {lock.unlock();}}
}

通过运行结果,得出结论:

  1. main 线程获取到锁
  2. t1 线程尝试获取锁,但是现在锁的拥有者是 main,所以 t1 线程进入阻塞状态 
  3. main 线程执行 interrupt 方法
  4. t1 线程被打断
2.2.2 将 lockInterruptibly 方法替换成 lock
@Slf4j(topic = "c.Test03")
public class Test03 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {log.info("lock");lock.lock();try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "t1");lock.lock();log.info(Thread.currentThread().getName() + " 获取到锁");t1.start();try {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {throw new RuntimeException(e);}log.info(Thread.currentThread().getName() + " 执行打断");t1.interrupt();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}} finally {lock.unlock();}}
}

通过运行结果,得出结论: 

  1. main 线程获取到锁
  2. t1 线程尝试获取锁,但是现在锁的拥有者是 main,所以 t1 线程进入阻塞状态 
  3. main 线程执行 interrupt 方法,t1 线程仍处于阻塞状态,并没有被打断
  4. 2 秒后 main 线程释放锁,这时候 t1 线程被唤醒,继续向下执行

2.3 可尝试(在指定时间段)获取锁

2.3.1 尝试获取锁,未获取到锁,直接返回
@Slf4j(topic = "c.Test04")
public class Test04 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {if (!lock.tryLock()) {log.info(Thread.currentThread().getName() + " 未获取到锁");return;}try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "t1");lock.lock();log.info(Thread.currentThread().getName() + " 获取到锁");t1.start();try {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}} finally {log.info(Thread.currentThread().getName() + " 解锁");lock.unlock();}}
}

通过运行结果,得出结论: 

  1. main 线程获取到锁
  2. t1 线程尝试获取锁,未获取到锁,直接返回
  3. 2 秒后 main 线程解锁
2.3.2 在指定时间内,尝试获取锁
@Slf4j(topic = "c.Test05")
public class Test05 {public static void main(String[] args) {ReentrantLock lock = new ReentrantLock();Thread t1 = new Thread(() -> {try {log.info(Thread.currentThread().getName() + "线程等待1分钟,尝试获取锁");if (!lock.tryLock(1, TimeUnit.MINUTES)) {log.info(Thread.currentThread().getName() + " 未获取到锁");return;}} catch (InterruptedException e) {throw new RuntimeException(e);}try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "t1");lock.lock();log.info(Thread.currentThread().getName() + " 获取到锁");t1.start();try {try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {throw new RuntimeException(e);}} finally {log.info(Thread.currentThread().getName() + " 解锁");lock.unlock();}}

通过运行结果,得出结论: 

  1. main 线程获取到锁
  2. t1 线程尝试获取锁,未获取到锁,等待1分钟
  3. 2 秒后 main 线程解锁
  4. 等待时间在 1 分钟内,t1 线程获取到锁

2.4 可设置公平(非公平)锁

2.4.1 设置非公平锁 (默认情况下,使用的是非公平锁)
@Slf4j(topic = "c.Test06")
public class Test06 {public static void main(String[] args) throws Exception {ReentrantLock lock = new ReentrantLock();log.info(Thread.currentThread().getName() + " 获取到锁");lock.lock();try {List<Thread> list = IntStream.range(0, 500).boxed().map(i ->new Thread(() -> {lock.lock();try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "t" + (i + 1))).collect(Collectors.toList());list.forEach(Thread::start);TimeUnit.MILLISECONDS.sleep(1000);new Thread(() -> {lock.lock();try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "插入线程").start();} finally {lock.unlock();}}
}

通过运行结果,得出结论: 

  1. main 线程获取到锁
  2. 启动 500 个线程(500个线程获取不到锁,进入阻塞状态)
  3. main 线程睡眠 1 秒,启动 “插入线程”(确保 “插入线程” 位于阻塞队列末尾)
  4. main 线程释放锁,默认情况下,ReentrantLock 是非公平锁,所以 “插入线程” 有提前运行的机会

PS:如果不是期望结果,可以运行多次

2.4.2 设置公平锁
@Slf4j(topic = "c.Test06")
public class Test06 {public static void main(String[] args) throws Exception {ReentrantLock lock = new ReentrantLock(true);log.info(Thread.currentThread().getName() + " 获取到锁");lock.lock();try {List<Thread> list = IntStream.range(0, 500).boxed().map(i ->new Thread(() -> {lock.lock();try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "t" + (i + 1))).collect(Collectors.toList());list.forEach(Thread::start);TimeUnit.MILLISECONDS.sleep(1000);new Thread(() -> {lock.lock();try {log.info(Thread.currentThread().getName() + " 获取到锁");} finally {lock.unlock();}}, "插入线程").start();} finally {lock.unlock();}}
}

通过运行结果,得出结论:  每次 “插入线程” 都是最后执行

2.5 支持多个变量

@Slf4j(topic = "c.Test07")
public class Test07 {private static final ReentrantLock LOCK = new ReentrantLock();private static final Condition FIRST_CONDITION = LOCK.newCondition();private static final Condition SECOND_CONDITION = LOCK.newCondition();private static volatile boolean FIRST_SWITCH = false;private static volatile boolean SECOND_SWITCH = false;private static void turnOnFirstSwitch() {LOCK.lock();try {log.info("turn on first switch");FIRST_SWITCH = true;FIRST_CONDITION.signal();} finally {LOCK.unlock();}}private static void turnOnSecondSwitch() {LOCK.lock();try {log.info("turn on first switch");SECOND_SWITCH = true;SECOND_CONDITION.signal();} finally {LOCK.unlock();}}public static void main(String[] args) throws Exception {new Thread(() -> {try {LOCK.lock();log.info(Thread.currentThread().getName() + " 获取到锁");while (!FIRST_SWITCH) {try {FIRST_CONDITION.await();} catch (InterruptedException e) {throw new RuntimeException(e);}}log.info(Thread.currentThread().getName() + " resume");} finally {LOCK.unlock();}}, "t1").start();new Thread(() -> {try {LOCK.lock();log.info(Thread.currentThread().getName() + " 获取到锁");while (!SECOND_SWITCH) {try {SECOND_CONDITION.await();} catch (InterruptedException e) {throw new RuntimeException(e);}}log.info(Thread.currentThread().getName() + " resume");} finally {LOCK.unlock();}}, "t2").start();log.info("main sleep");TimeUnit.SECONDS.sleep(1);turnOnFirstSwitch();log.info("main sleep");TimeUnit.SECONDS.sleep(1);turnOnSecondSwitch();}
}

通过运行结果,得出结论: 

  1. main 线程启动 t1、t2 线程,并进入睡眠
  2. t1、t2 线程启动后,分别获取到锁,但因为条件不满足,进入阻塞状态
  3. 1 秒后 main 线程执行 turnOnFirstSwitch 方法,将变量 FIRST_SWITCH 设为 true,并再次睡眠,此时 t1 线程条件满足,继而被唤醒
  4. main 线程睡眠 1 秒后, main 线程执行 turnOnSecondSwitch 方法,将变量 SECOND_SWITCH 设为 true,t2 线程条件满足,继而被唤醒

http://www.mrgr.cn/news/63135.html

相关文章:

  • VLAN使用实验
  • goframe开发一个企业网站 生成代码3
  • 虚拟滚动 - 从基本实现到 Angular CDK
  • Unity3D PCG地图的使用与优化详解
  • Cocos使用精灵组件显示相机内容
  • AI学习指南自然语言处理篇-Transformer模型的实践
  • 一文详解精细化工行业持续增长的策略与路径解析
  • ES8388 —— 带耳机放大器的低功耗立体声音频编解码器(2)
  • 中药怎么计价?中药如何复制药方就可以快速计算出金额?
  • 【蓝队技能】【溯源反制】社会工程学
  • 校车购票微信小程序ssm+论文源码调试讲解
  • final方法可以被重载吗?
  • 在多模块应用中使用navigation不知不觉都是这么用
  • NeurIPS 2024 Oral:用 DuQuant 实现 SOTA 4bit 量化
  • 浏览器的异步行为导致多个文件下载时没有全部执行
  • 微服务基础拆分实践(第一篇)
  • 【Linux 从基础到进阶】分布式文件系统的高可用配置
  • DAYWEB69 攻防-Java 安全JWT 攻防Swagger 自动化算法签名密匙Druid 泄漏
  • 关于解决keil中出现乱码的情况处理,搜索框乱码
  • 什么是Javascript,有什么特点
  • 计算机毕业设计——ssm基于微信平台的校园汉服租赁系统的设计与实现演示录像2021微信端
  • XXXX 本地模型替换为 两家 API
  • 环境变量——用户变量和系统变量
  • (实战)WebApi第9讲:EFCore性能优化(IQueryable延迟查询、取消跟踪机制)
  • Python爬虫必备利器:urllib库全面解析
  • 在树莓派 Raspbian 11 上使用 pyenv 安装 Python 3.9