什么是可中断锁?有什么用?怎么实现?

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone

在 Java 中有两种锁,一种是内置锁 synchronized,一种是显示锁 Lock,其中 Lock 锁是可中断锁,而 synchronized 则为不可中断锁。

所谓的中断锁指的是锁在执行时可被中断,也就是在执行时可以接收 interrupt 的通知,从而中断锁执行

PS:默认情况下 Lock 也是不可中断锁,但是可以通过特殊的“手段”,可以让其变为可中断锁,接下来我们一起来看。

为什么需要可中断锁?

不可中断锁的问题是,当出现“异常”时,只能一直阻塞等待,别无其他办法,比如下面这个程序。下面的这个程序中有两个线程,其中线程 1 先获取到锁资源执行相应代码,而线程 2 在 0.5s 之后开始尝试获取锁资源,但线程 1 执行时忘记释放锁了,这就造成线程 2 一直阻塞等待的情况,实现代码如下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class InterruptiblyExample {public static void main(String[] args) {Lock lock = new ReentrantLock();// 创建线程 1Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {lock.lock();System.out.println("线程 1:获取到锁.");// 线程 1 未释放锁}});t1.start();// 创建线程 2Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// 先休眠 0.5s,让线程 1 先执行try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}// 获取锁System.out.println("线程 2:等待获取锁.");lock.lock();try {System.out.println("线程 2:获取锁成功.");} finally {lock.unlock();}}});t2.start();}
}

以上代码执行的结果如下:

从上述结果可以看出,此时线程 2 在等待获取锁的操作,然而经历了 N 久之后...再次查看结果,依然是熟悉的画面:

线程 2 还在阻塞等待获取线程 1 释放锁资源,此时的线程 2 除了等之外,并无其他方法。

并且,当我们熟练地拿出了 JConsole,试图得到一个死锁的具体信息时,却得到了这样的结果:

并没有检测到任何死锁信息,从上图我们可以看出,当只有一个锁资源的时候,系统并不会把这种情况判定为死锁,当然也没有阻塞等待的具体信息喽,此时只剩下线程 2 孤单地等待着它的“锁儿”。

使用中断锁

然而,中断锁的出现,就可以打破这一僵局,它可以在等待一定时间之后,主动的中断线程 2,以解决线程阻塞等待的问题。

中断锁的核心实现代码是 lock.lockInterruptibly() 方法,它和 lock.lock() 方法作用类似,只不过使用 lockInterruptibly 方法可以优先接收中断的请求,中断锁的具体实现如下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class InterruptiblyExample {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock();// 创建线程 1Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {// 加锁操作lock.lock();System.out.println("线程 1:获取到锁.");} catch (InterruptedException e) {e.printStackTrace();}// 线程 1 未释放锁}});t1.start();// 创建线程 2Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// 先休眠 0.5s,让线程 1 先执行try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}// 获取锁try {System.out.println("线程 2:尝试获取锁.");lock.lockInterruptibly(); // 可中断锁System.out.println("线程 2:获取锁成功.");} catch (InterruptedException e) {System.out.println("线程 2:执行已被中断.");}}});t2.start();// 等待 2s 后,终止线程 2Thread.sleep(2000);if (t2.isAlive()) { // 线程 2 还在执行System.out.println("执行线程的中断.");t2.interrupt();} else {System.out.println("线程 2:执行完成.");}}
}

以上代码执行结果如下:

从上述结果可以看出,当我们使用了 lockInterruptibly 方法就可以在一段时间之后,判断它是否还在阻塞等待,如果结果为真,就可以直接将他中断,如上图效果所示。

但当我们尝试将 lockInterruptibly 方法换成 lock 方法之后(其他代码都不变),执行的结果就完全不一样了,实现代码如下:

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class InterruptiblyExample {public static void main(String[] args) throws InterruptedException {Lock lock = new ReentrantLock();// 创建线程 1Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {// 加锁操作lock.lockInterruptibly();System.out.println("线程 1:获取到锁.");} catch (InterruptedException e) {e.printStackTrace();}// 线程 1 未释放锁}});t1.start();// 创建线程 2Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {// 先休眠 0.5s,让线程 1 先执行try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}// 获取锁try {System.out.println("线程 2:尝试获取锁.");lock.lock();System.out.println("线程 2:获取锁成功.");} catch (Exception e) {System.out.println("线程 2:执行已被中断.");}}});t2.start();// 等待 2s 后,终止线程 2Thread.sleep(2000);if (t2.isAlive()) { // 线程 2 还在执行System.out.println("执行线程的中断.");t2.interrupt();} else {System.out.println("线程 2:执行完成.");}}
}

以上程序执行结果如下:

从上图可以看出,当使用 lock 方法时,即使调用了 interrupt 方法依然不能将线程 2 进行中断。

总结

本文介绍了中断锁的实现,通过显示锁 Lock 的 lockInterruptibly 方法来完成,它和 lock 方法作用类似,但 lockInterruptibly 可以优先接收到中断的通知,而 lock 方法只能“死等”锁资源的释放,同时这两个方法的区别也是常见的面试题,希望本文对你有用。

---END---


原创并发文章推荐

1.线程的故事:我的3位母亲成就了优秀的我!

2.线程池的7种创建方式,强烈推荐你用它...

3.轻量级锁一定比重量级锁快吗?

4.这样终止线程,竟然会导致服务宕机?

5.漫画:如何证明sleep不释放锁,而wait释放锁?

6.池化技术到达有多牛?看了这个对比吓我一跳!

7.求求你,别再用wait和notify了!

8.Semaphore自白:限流器用我就对了!

9.CountDownLatch:别浪,等人齐再团!

10.CyclicBarrier:人齐了,老司机就发车了!

11.Java中用户线程和守护线程区别这么大?

12.ThreadLocal不好用?那是你没用对!

13.ThreadLocal内存溢出代码演示和原因分析!

14.SimpleDateFormat线程不安全的5种解决方案!

15.synchronized 加锁 this 和 class 的区别!

16.synchronized 优化手段之锁膨胀机制!

17.synchronized 中的 4 个优化,你知道几个?

18.ReentrantLock 中的 4 个坑!

19.图解:为什么非公平锁的性能更高?

20.死锁的 4 种排查工具 !

21.死锁终结者:顺序锁和轮询锁!

22.轮询锁使用时遇到的问题与解决方案!

23.一文详解死锁!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544578.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10个经典又容易被人疏忽的JVM面试题

前言整理了10个经典又容易被疏忽的JVM面试题,谢谢阅读,大家加油哈.1. 对象一定分配在堆中吗?有没有了解逃逸分析技术?「对象一定分配在堆中吗?」 不一定的,JVM通过「逃逸分析」,那些逃不出方法的…

面试官:怎么解决MySQL中的死锁问题?

咱们使用 MySQL 大概率上都会遇到死锁问题,这实在是个令人非常头痛的问题。本文将会对死锁进行相应介绍,对常见的死锁案例进行相关分析与探讨,以及如何去尽可能避免死锁给出一些建议。话不多说,开整!什么是死锁死锁是并…

ubuntu双系统导致进windows花屏

2019独角兽企业重金招聘Python工程师标准>>> 5600U的集成显卡,装了ubuntu的双系统,居然导致进win7的时候花屏,度娘狗哥都不得求解 网上很多解决方法都说在启动时加上nomodeset,发现对ubuntu15没用,且失去了…

工作总结:日志打印的15个建议

前言 日志是快速定位问题的好帮手,是撕逼和甩锅的利器!打印好日志非常重要。今天我们来聊聊日志打印的15个好建议~1. 选择恰当的日志级别 常见的日志级别有5种,分别是error、warn、info、debug、trace。日常开发中,我们需要选择恰…

MFC属性页对话框

属性页对话框 分类 分页和引导 类 CPropertyPage-父亲CDialog类别,所谓的属性页或网页对话框。 CPropertySheet-父类是CWnd,称为属性表单。 一个完整的属性页对话框由一个属性表单多个属性页组成。属性页嵌套在属性表单内。 标签式属性页的创建步骤&…

面试官:ConcurrentHashMap为什么放弃了分段锁?

今天我们来讨论一下一个比较经典的面试题就是 ConcurrentHashMap 为什么放弃使用了分段锁,这个面试题阿粉相信很多人肯定觉得有点头疼,因为很少有人在开发中去研究这块的内容,今天阿粉就来给大家讲一下这个 ConcurrentHashMap 为什么在 JDK8 …

C语言函数指针的应用——自制谐波分析软件

文章目录函数指针简介格式介绍颜色头文件计算机仿真使用说明完整代码部分效果图函数指针简介 如果在一个大型C语言程序中要反复调用函数,而调用的函数又不明确时,函数指针就是一个非常有用的东西。如果你的函数体内可以传递不同的函数,那就非…

PHP5.5四种序列化性能对比

2019独角兽企业重金招聘Python工程师标准>>> 结论: 1、小数组用msgpack,无论空间和性能都最好 2、大数组,考虑空间用igbinary,考虑性能用msgpack json_encode,serialize,igbinary,msgpack四种序列化方式&am…

MyBatis Plus 批量数据插入功能,yyds!

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone最近 Review 小伙伴代码的时候,发现了一个小小的问题,小伙伴竟然在 for 循环中进行了 insert (插入&a…

C语言打印彩色字符——以(枚举法+字符串查找)为例展示

文章目录C语言颜色头文件——自制非常简单的调用函数实战演练——一个基础的枚举变量小程序牛刀小试——查找字符小程序C语言颜色头文件——自制非常简单的调用函数 显然,C语言是不会提供打印彩色字符的标准函数,而我们有时候为了强调C语言打印的部分字…

再见 Spring Task,这个定时任务框架真香!

最近有朋友问到定时任务相关的问题。于是,我简单写了一篇文章总结一下定时任务的一些概念以及一些常见的定时任务技术选型。希望能对小伙伴们有帮助!个人能力有限。如果文章有任何需要补充/完善/修改的地方,欢迎在评论区指出,共同…

C语言实现动画控制

文章目录原材料说明一场革命原材料 下载原材料网址: https://www.easyx.cn/downloads/ 下载easyx2014冬至版,将lib文件放在编译器默认的lib文件夹,h头文件放在编译器默认的include文件夹即可 说明 C语言可以用系统内部的定时函数sleep和usleep定时(需…

聊聊redis分布式锁的8大坑

前言在分布式系统中,由于redis分布式锁相对于更简单和高效,成为了分布式锁的首先,被我们用到了很多实际业务场景当中。但不是说用了redis分布式锁,就可以高枕无忧了,如果没有用好或者用对,也会引来一些意想…

MyBatis 批量插入数据的 3 种方法!

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone批量插入功能是我们日常工作中比较常见的业务功能之一,之前我也写过一篇关于《MyBatis Plus 批量数据插入功能,yy…

MongoDB: The Definitive Guide

第一章 简介 MongoDB是面向文档的数据库,不是关系型数据库。内置对MapReduce的支持,以及对地理空间索引的支持。 丰富的数据模型容易扩展,它所采用的面向文档的数据模型可以使其在多台服务器之间分割数据丰富的功能,索引、存储Jav…

Python联网下载文件

声明 Python版本2.7.3所需Py文件——urllib22.7.3版本的Python Shell即可直接执行,但需要联网若程序执行成功,则会下载以下网址的txt文本并打印在shell中 http://helloworldbook2.com/data/message.txt 本代码来源于《父与子的编程之旅——与小卡特一起…

如何给SpringBoot配置轻松加密?

在实践中,项目的某些配置信息是需要进行加密处理的,以减少敏感信息泄露的风险。比如,在使用Druid时,就可以基于它提供的公私钥加密方式对数据库的密码进行加密。但更多时候,比如Redis密码、MQ密码等敏感信息&#xff0…

C语言将循环小数/有限小数转换为分数

文章目录数学基础编程思路代码数学基础 早在小学的时候我就对循环小数非常感兴趣,加上初中和高中对循环小数可以说有一定基础研究,因此想到写一个将循环下小数转换为分数的程序,非常有意思,并且对初学者来说,它的输入…

升级了 Windows 11 正式版,有坑吗?

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)今天磊哥去公司上班,惊喜的发现 Windows 提示更新了,并且是 Windows 11 正式版,这太让人…

C语言结构体的应用——万年历

文章目录万年历简述代码万年历简述 万年历——就是输入一个日期可以查询是星期几,这个功能看起来很普通,但是如果用程序时间的话,还是药费一番周折: 我们需要保存一个固定的日期,存放它是星期几,输入一个自定义的日期…