admin 管理员组

文章数量: 1087678

Java 进阶—死锁造成原因及其解决

今天我们来了解一下线程死锁,死锁很好理解,从字面上来看就是锁死了,解不开,在大街上看到一对卧龙凤雏的情侣,怎么说,你们给我锁死,不要分开去霍霍别人

之前我们不是说过,解决线程安全的方法就是给线程上锁,java进阶—线程安全问题,但上锁也会有死锁的情况

那么,死锁是什么?先举个通俗点的例子 小明跟小红分别同时参加两个会议,这时候办公室刚好只有一台笔记本(在小红手上),一台投影仪(在小明手上),这是两个都想要对方的东西,两人互不相让,开始争执,这样都开不成会议,就形成了死锁


把小明跟小红换成两个线程,所以,一句话,死锁就是两个或两个以上的线程争夺彼此的锁,造成阻塞

从这里我们也可以看到死锁产生的条件

  • 首先,死锁产生需要两个或者两个以上线程 (例子中的小明跟小红)

  • 两个或者两个以上的锁 (例子中的 笔记本跟投影仪)

  • 两个或两个以上线程持有不同锁(例子中小明有投影仪,小红有笔记本)

  • 持有不同锁线程争夺对方的锁 (例子中小明跟小红抢对方的东西)

用代码来解释上述例子

我们按照死锁的四个条件一步一步来

1. 首先有两个线程

一个小明类,一个小红类

2. 两个锁

小明类 里面 有 投影仪锁 , 小红类里面有电脑锁

3. 两个线程持有不同的锁

小明重写run 方法 调用 投影仪 锁

小红重写run 方法 调用 笔记本 锁

4. 持有不同锁调用对方的锁

小明在projector 里面去调用 小红的computer

小红在computer里面去调用 小红的projector


public class XiaoMing  implements Runnable{@Overridepublic void run() {//小明持有投影仪这个锁projector();}/***  投影仪这个锁  static 一个资源*/public static synchronized  void projector() {try {//线程休眠2秒,目的为了执行慢一点,更方便看出效果Thread.sleep(2000);} catch (InterruptedException e) { e.printStackTrace();}System.out.println("小明的投影仪"); // 获取小红的锁XiaoHongputer();}
}
public class XiaoHong  implements Runnable{@Overridepublic void run() { //小红持有笔记本这个锁computer();}/*** 笔记本这个锁   static 一个资源*/public static synchronized  void computer() {try {//线程休眠2秒Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("小红的笔记本");//获取小明的锁XiaoMing.projector();}
}

我们去启动这两个线程

    public static void main(String[] args) {//创建小红线程XiaoHong xiaoHong =new XiaoHong();Thread thread =new Thread(xiaoHong);//创建小明线程XiaoMing xiaoMing = new XiaoMing();Thread thread1 =new Thread(xiaoMing);//启动thread.start();thread1.start();}

执行结果:


程序在打印两个结果后,两个程序在相互争夺资源,程序停止需要借助外力

解决死锁也就很简单了,我们不去拿对方的锁就好了


这一步,我们不去调用对方的锁,

看看效果:

可以看到程序正常终止了

要是不太明白,我们再来看一个更直观的代码:(注意看代码中的注释)

还是有这么两个资源,电脑跟投影仪


public class Person implements Runnable {/*** 只有一份资源,电脑  跟 投影仪*/static final Computer COMPUTER = new Computer();static final Projector PROJECTOR = new Projector();/*** 类型 0 代表 小红  1代表小明*/int  type;/*** 人名*/String  name;public Person(int type, String name) {this.type = type;this.name = name;}@Overridepublic void run() {//开始抢占资源try {getSource();} catch (InterruptedException e) {e.printStackTrace();}}private void getSource() throws InterruptedException {//让小明先拿投影仪if (type==1) {synchronized (PROJECTOR) {System.out.println(this.name+"获得投影仪");Thread.sleep(2000);//小明拿到投影仪,又想去拿电脑synchronized (COMPUTER) {System.out.println(this.name+"获得电脑");}}}else {//小红拿到电脑,又想去拿投影仪synchronized (COMPUTER) {System.out.println(this.name+"获得电脑");Thread.sleep(1000);synchronized (PROJECTOR) {System.out.println(this.name+"获得投影仪");}}}}}

启动两个线程

  public static void main(String[] args) {Person person =new Person(1,"小明");Person person2 =new Person(0,"小红");Thread thread =new Thread(person);Thread thread1 =new Thread(person2);thread.start();thread1.start();}

执行结果:

程序进入了死锁

怎么解决?一样的,获取到锁的同时不去调用对方的锁,我们这两段代码放到外边来


变成下面这样

package com.xrp.flinkDemo.demo;public class Person implements Runnable {/*** 只有一份资源,电脑  跟 投影仪*/static final Computer COMPUTER = new Computer();static final Projector PROJECTOR = new Projector();/*** 类型 0 代表 小红  1代表小明*/int  type;/*** 人名*/String  name;public Person(int type, String name) {this.type = type;this.name = name;}@Overridepublic void run() {//开始抢占资源try {getSource();} catch (InterruptedException e) {e.printStackTrace();}}private void getSource() throws InterruptedException {//让小明先拿投影仪if (type==1) {synchronized (PROJECTOR) {System.out.println(this.name+"获得投影仪");Thread.sleep(2000);}synchronized (COMPUTER) {System.out.println(this.name+"获得电脑");}}else {//小红拿到电脑,又想去拿投影仪synchronized (COMPUTER) {System.out.println(this.name+"获得电脑");Thread.sleep(1000);}synchronized (PROJECTOR) {System.out.println(this.name+"获得投影仪");}}}}

再执行看看:

可以发现解决了死锁问题,并且都获得了想要的资源

好了,以上便是死锁原因以及避免死锁方法了,主要就是不要在持有一个锁里面再去获取别的锁

【完】

本文标签: Java 进阶死锁造成原因及其解决