概念

死锁

是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。 由于资源占用是互斥的,当某个进程提出申请资源后,使得有关进程在无外力协助下,永远分配不到必需的资源而无法继续运行,这就产生了一种特殊现象:死锁。”

活锁(英文 livelock)

指事物1可以使用资源,但它让其他事物先使用资源;事物2可以使用资源,但它也让其他事物先使用资源,于是两者一直谦让,都无法使用资源。

避免活锁的简单方法是采用先来先服务的策略。当多个事务请求封锁同一数据对象时,封锁子系统按请求封锁的先后次序对事务排队,数据对象上的锁一旦释放就批准申请队列中第一个事务获得锁。

示例一

使用tryLock()方法来防止多线程死锁。

tryLock()方法:尝试获取一把锁,如果获取成功返回true,如果还拿不到锁,就返回false。

public class DeadLock1 {private static Lock lock1 = new ReentrantLock();private static Lock lock2 = new ReentrantLock();public static void deathLock() {new Thread() {@Overridepublic void run() {while (true) {if (lock1.tryLock()) {try {//如果获取成功则执行业务逻辑,如果获取失败,则释放lock1的锁,自旋重新尝试获得锁if (lock2.tryLock()) {try {System.out.println("Thread1:已成功获取 lock1 and lock2 ...");break;} finally {lock2.unlock();}}} finally {lock1.unlock();}}System.out.println("Thread1:获取锁失败,重新获取---");try {//防止发生活锁TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {@Overridepublic void run() {while (true) {if (lock2.tryLock()) {try {//如果获取成功则执行业务逻辑,如果获取失败,则释放lock2的锁,自旋重新尝试获得锁if (lock1.tryLock()) {try {System.out.println("Thread2:已成功获取 lock2 and lock1 ...");break;} finally {lock1.unlock();}}} finally {lock2.unlock();}}System.out.println("Thread2:获取锁失败,重新获取---");try {//防止发生活锁TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 5; i++) {deathLock();}}
}

该示例启动两个线程。线程1首先获取lock1的锁,然后再获取lock2的锁;线程2首先获取lock2的锁,然后再获取lock1的锁。这样如果这时线程1获得了lock1的锁,同时线程2获得lock2的锁,然后线程1尝试去获得lock2的锁,线程2尝试获得线程1的锁,就会造成死锁。

我们这里使用tryLock来获取两个锁,如果一个线程不能同时获取两把锁,那么就回退并自旋重新尝试(使用while循环)。再使用TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));随机休眠一段时间,从而降低发生活锁的可能性。如果处理成功,则使用break跳出循环。

示例二

使用tryLock(long timeout, TimeUnit unit) 方法来防止多线程死锁。

tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。

import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class DeadLock2 {private static ReentrantLock lock1 = new ReentrantLock();private static ReentrantLock lock2 = new ReentrantLock();public static void deathLock() {new Thread() {@Overridepublic void run() {while (true) {try {if (lock1.tryLock(10, TimeUnit.MILLISECONDS)) {try {//如果获取成功则执行业务逻辑,如果获取失败,则释放lock1的锁,自旋重新尝试获得锁if (lock2.tryLock(10, TimeUnit.MILLISECONDS)) {System.out.println("Thread1:已成功获取 lock1 and lock2 ...");break;}} catch (InterruptedException e) {e.printStackTrace();} finally {if(lock2.isHeldByCurrentThread()){lock2.unlock();}}}} catch (InterruptedException e) {e.printStackTrace();} finally {if(lock1.isHeldByCurrentThread()){lock1.unlock();}}System.out.println("Thread1:获取锁失败,重新获取---");try {TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}}}}.start();new Thread() {@Overridepublic void run() {while (true) {try {if (lock2.tryLock(10, TimeUnit.MILLISECONDS)) {try {//如果获取成功则执行业务逻辑,如果获取失败,则释放lock1的锁,自旋重新尝试获得锁if (lock1.tryLock(10, TimeUnit.MILLISECONDS)) {System.out.println("Thread2:已成功获取 lock2 and lock1 ...");break;}} catch (InterruptedException e) {e.printStackTrace();} finally {if(lock1.isHeldByCurrentThread()){lock1.unlock();}}}} catch (InterruptedException e) {e.printStackTrace();} finally {if(lock2.isHeldByCurrentThread()){lock2.unlock();}}System.out.println("Thread2:获取锁失败,重新获取---");try {TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}}}}.start();}public static void main(String[] args) throws InterruptedException {for (int i = 0; i < 5; i++) {deathLock();}}
}

该示例同示例一。我们这里使用tryLock(long time, TimeUnit unit)来获取两个锁,如果一个线程不能同时获取两把锁,那么就回退并自旋重新尝试(使用while循环)。在使用TimeUnit.NANOSECONDS.sleep(new Random().nextInt(100));随机休眠一段时间,从而降低发生活锁的可能性。如果处理成功,则使用break跳出循环。

示例三

使用lockInterruptibly()获得锁,如果发生死锁,调用线程interrupt来消除死锁。

ReentrantLock.lockInterruptibly允许在等待时由其它线程调用等待线程的Thread.interrupt方法来中断等待线程的等待而直接返回,这时不用获取锁,而会抛出一个InterruptedException。而ReentrantLock.lock方法不允许Thread.interrupt中断,即使检测到Thread.isInterrupted,一样会继续尝试获取锁,失败则继续休眠。只是在最后获取锁成功后再把当前线程置为interrupted状态。

public class DeadLock3 {private static Lock lock1 = new ReentrantLock();private static Lock lock2 = new ReentrantLock();public static void deathLock() {new Thread() {@Overridepublic void run() {try {lock1.lockInterruptibly();try {TimeUnit.SECONDS.sleep(1);lock2.lockInterruptibly();System.out.println("thread 1 ...");} catch (InterruptedException e) {e.printStackTrace();} finally {lock2.unlock();}} catch (InterruptedException e1) {e1.printStackTrace();} finally {lock1.unlock();}}}.start();new Thread() {@Overridepublic void run() {try {lock2.lockInterruptibly();try {TimeUnit.SECONDS.sleep(1);lock1.lockInterruptibly();System.out.println("thread 1 ...");} catch (InterruptedException e) {e.printStackTrace();} finally {lock1.unlock();}} catch (InterruptedException e1) {e1.printStackTrace();} finally {lock2.unlock();}}}.start();}public static void main(String[] args) throws InterruptedException {deathLock();TimeUnit.SECONDS.sleep(2);checkDeadLock();}//基于JMX获取线程信息public static void checkDeadLock() {//获取Thread的MBeanThreadMXBean mbean = ManagementFactory.getThreadMXBean();//查找发生死锁的线程,返回线程id的数组long[] deadLockThreadIds = mbean.findDeadlockedThreads();System.out.println("---" + deadLockThreadIds);if (deadLockThreadIds != null) {//获取发生死锁的线程信息ThreadInfo[] threadInfos = mbean.getThreadInfo(deadLockThreadIds);//获取JVM中所有的线程信息Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();for (Entry<Thread, StackTraceElement[]> entry : map.entrySet()) {for (int i = 0; i < threadInfos.length; i++) {Thread t = entry.getKey();if (t.getId() == threadInfos[i].getThreadId()) {//中断发生死锁的线程t.interrupt();//打印堆栈信息       // for (StackTraceElement ste : entry.getValue()) {// // System.err.println("t" + ste.toString().trim());// }}}}}}
}

我们这里使用lockInterruptibly()方法来获取锁,我们这里使用线程1获取lock1 休眠1秒,嘻嘻按错2获取lock2 休眠1秒,1秒过后,然后线程1再获取lock2,线程2再去获得lock1就会发生死锁。这是我们又执行了checkDeadLock()方法,来检查JVM中是否有死锁,如果有死锁,则把发生死锁的线程执行interrupt()方法,使该线程响应中断,从而避免发生死锁。(实际应用中,检查死锁可以单独开启一个daemon线程,每间隔一段时间检查一下是否发生死锁,如果有则预警、记录日志、或中断该线程避免死锁)

本人简书blog地址:http://www.jianshu.com/u/1f0067e24ff8    
点击这里快速进入简书

GIT地址:http://git.oschina.net/brucekankan/
点击这里快速进入GIT

基于ReentrantLock发生死锁的解决方案相关推荐

  1. w3wp进程发生死锁ISAPI aspnet

    ISAPI c:\windows\microsoft.net\framework\v2.0.50727\aspnet_isapi.dll 报告它自身有问题,原因如下: Deadlock detecte ...

  2. 单例设计模式-静态内部类-基于类初始化的延迟加载解决方案及原理解析

    刚刚线程1看不到线程0的重排序,我们创建一个类,这个方案是使用静态内部类来解决,一会我们也会分析一下原理,我们创建一个静态内部类,静态内部类的代理模式,JVM在类的初始化阶段,也就是class被加载后 ...

  3. Mysql 死锁和死锁的解决方案

    最近在研究Mysql底层原理,研究到了死锁,感觉挺有意思,在这里和大家分享一下 前置知识:需要了解锁的种类,如表锁.行锁:行锁又分为记录锁.间隙锁.临键锁等等:什么情况下会加表锁,什么情况下会加行锁, ...

  4. Django中的跨域解决办法 基于后端的跨域解决方案

    Django中的跨域解决办法 基于后端的跨域解决方案 1 何为跨域 在浏览器中,只要发送请求的URL的协议.域名.端口号这三者中的任意一个与当前页面地址的协议.域名.端口号不同,则称之为跨域.当发生这 ...

  5. Java中的死锁及其解决方案

    Java中的死锁及其解决方案 在Java中,锁是很常见的一个工具,常见的用法就是在高并发场景下保证线程安全,但是使用不当也会造成死锁,给我们带来一些不必要的麻烦,本文分析死锁及其产生原因,并作出相应的 ...

  6. 线上发生死锁异常了,该怎么办

    前言 MySQL 死锁异常是我们经常会遇到的线上异常类别,一旦线上业务日间复杂,各种业务操作之间往往会产生锁冲突,有些会导致死锁异常.这种死锁异常一般要在特定时间特定数据和特定业务操作才会复现,并且分 ...

  7. 构建一个基本的前端自动化开发环境 —— 基于 Gulp 的前端集成解决方案(四)

    构建一个基本的前端自动化开发环境 -- 基于 Gulp 的前端集成解决方案(四) 参考文章: (1)构建一个基本的前端自动化开发环境 -- 基于 Gulp 的前端集成解决方案(四) (2)https: ...

  8. badboy页面脚本发生错误,解决方案

    badboy页面脚本发生错误,解决方案 参考文章: (1)badboy页面脚本发生错误,解决方案 (2)https://www.cnblogs.com/ouyy/p/yaoyao.html (3)ht ...

  9. REST API 基于ACCESS TOKEN 的权限解决方案

    REST API 基于ACCESS TOKEN 的权限解决方案 参考文章: (1)REST API 基于ACCESS TOKEN 的权限解决方案 (2)https://www.cnblogs.com/ ...

最新文章

  1. PHP一些十分严重的缺陷
  2. System Landscape Recommendations for SAP Customer Activity Repository
  3. Windows下删除Kafka中某个Group
  4. sql server服务看不到,显示为远程过程调用在与 SQL Server 建立连接时出现与网络相关的或特定于实例的错误10061...
  5. CrazyWing:Python自动化运维开发实战 九、Python数据类型之列表
  6. SAP财务模块中,科目余额表中的数据存在数据库的哪张表中?
  7. [Android 插件化(一)] DynamicLoadApk的用法
  8. centos安装anaconda_每天三分钟之TensorFlow学习03:Win下安装TF2
  9. yii不能没有提示验证错误信息_安装Mac系统时提示”应用程序副本不能验证 它在下载过程...”的解决方法...
  10. Redkale 让你重新认识Java
  11. 鸡头稳如云台_三轴增稳云台是怎么让相机、手机「稳如鸡头」的?
  12. 了解java虚拟机mdash;串行回收器(6)
  13. 局域网文件共享手机访问电脑文件软件
  14. 【MOOC测试】数学模型
  15. 移动、联通、电信三大运营商手机号段大全 附手机号正则表达式
  16. 【转】MapGIS K9基础系列(二)
  17. 90KB的3D游戏(像CS一样)[汇编界又一奇迹]绝对震撼,操作和CS一样!
  18. android蓝牙打印机打印图片,如何使用打印机(通过蓝牙打印)从Android设备打印图像和一些数据?...
  19. 数据分析方法:非正态数据转化成正态数据
  20. 星露谷物语联机服务器位置已满,星露谷物语联机版

热门文章

  1. 十六、爬虫实战,多线程抓取毛豆新车的数据
  2. 三十八、Scrapy 下载中间件Middleware
  3. 从多篇2021年顶会论文看多模态预训练模型最新研究进展
  4. ICML 2021 | Option-GAI: 机器人任务太长太复杂?不妨试试层次化模仿学习
  5. 手把手教你薅羊毛,1 元体验业内领先AI技术!
  6. Attention注意力机制的前世今身
  7. 胶囊网络(Capsule Network)在文本分类中的探索
  8. CVPR2017有哪些值得读的Image Caption论文?
  9. Numpy-矩阵的合并
  10. java 泛型 引用_Java嵌套自引用泛型