Java中的死锁与活锁

/** 面包 */

private static final Object bread = new Object();

/** 水 */

private static final Object water = new Object();

@Override

public void run() {

if (flag == 1) {

// 先吃面包再喝水,环路等待条件

synchronized (bread) {

try {

Thread.sleep(500);

} catch (Exception e) {

e.printStackTrace();

}

System.out.println(“面包吃了,等喝水。。。”);

synchronized (water) {

System.out.println(“面包吃了,水也喝到了”);

}

}

}

if (flag == 0) {

// 先喝水再吃面包,环路等待条件

synchronized (water) {

try {

Thread.sleep(500);

} catch (Exception e) {

e.printStackTrace();

}

System.out.println(“喝完水,等吃面包了。。。”);

synchronized (bread) {

System.out.println(“喝完水,面包也吃完了。。。”);

}

}

}

}

public static void main(String[] args) {

DeadLock lockBread = new DeadLock();

DeadLock lockWater = new DeadLock();

lockBread.flag = 1;

lockWater.flag = 0;

// lockBread,lockWater 都处于可执行状态,但 JVM 线程调度先执行哪个线程是不确定的。

// lockWater 的 run() 可能在 lockBread 的 run() 之前运行

new Thread(lockBread).start();

new Thread(lockWater).start();

}

}

程序运行结果:

喝完水,等吃面包了。。。

面包吃了,等喝水。。。

死锁排查

先执行上面的代码,再进行排查!

1、使用 jps -l

D:\workspace-code\gitee\dolphin>jps -l

7072 org.jetbrains.jps.cmdline.Launcher

8824 sun.tools.jps.Jps

17212

4012 com.dolphin.thread.locks.DeadLock

6684 org.jetbrains.idea.maven.server.RemoteMavenServer36

4012 com.dolphin.thread.locks.DeadLock,粘贴进程号 4012

2、使用 jstack 4012

… …

Java stack information for the threads listed above:

===================================================

“Thread-1”:

at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:40)

  • waiting to lock <0x000000076bf9e3d8> (a java.lang.Object)

  • locked <0x000000076bf9e3e8> (a java.lang.Object)

at java.lang.Thread.run(Thread.java:748)

“Thread-0”:

at com.dolphin.thread.locks.DeadLock.run(DeadLock.java:26)

  • waiting to lock <0x000000076bf9e3e8> (a java.lang.Object)

  • locked <0x000000076bf9e3d8> (a java.lang.Object)

at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.

从打印信息我们可以看出:

  1. Found 1 deadlock: 发现一个死锁;

  2. Thread-1 locked <0x000000076bf9e3e8> waiting to lock <0x000000076bf9e3d8>:线程1,锁住了 0x000000076bf9e3e8 等待 0x000000076bf9e3d8 锁;

  3. Thread-0 locked <0x000000076bf9e3d8> waiting to lock <0x000000076bf9e3e8>:线程0,锁住了 0x000000076bf9e3d8 等待 0x000000076bf9e3e8 锁;

image-20220103152621661

总结一下

  1. 当 DeadLock 类的对象 flag=1 时(lockBread),先锁定 bread,睡眠500毫秒;

  2. 而 lockBread 在睡眠的时候另一个 flag==0 的对象(lockWater)线程启动,先锁定 water,睡眠500毫秒;

  3. lockBread 睡眠结束后需要锁定 water 才能继续执行,而此时 water 已被 lockWater 锁定;

  4. lockWater 睡眠结束后需要锁定 bread 才能继续执行,而此时 bread 已被 lockBread 锁定;

  5. lockBread、lockWater 相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁;

如何避免死锁


预防死锁

其实就是破坏死锁的四个必要条件!!!

  • 破坏互斥条件:使资源同时访问而非互斥使用,就没有进程会阻塞在资源上,从而不发生死锁。

  • 破坏请求和保持条件:采用静态分配的方式,静态分配的方式是指进程必须在执行之前就申请需要的全部资源,且直至所要的资源全部得到满足后才开始执行,只要有一个资源得不到分配,也不给这个进程分配其他的资源。

  • 破坏不剥夺条件:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源,但是只适用于内存和处理器资源。

  • 破坏循环等待条件:给系统的所有资源编号,规定进程请求所需资源的顺序必须按照资源的编号依次进行。

设置加锁顺序

两个线程试图以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁,那么就不会出现循环的加锁依赖性,因此也就不会产生死锁,每个需要锁面包和锁水的线程都以相同的顺序来获取面包和水,那么就不会发生死锁了,如下图所示:

image-20220103154444206

根据上图我们修改一下之前写的死锁代码,消除死锁!

@Override

public void run() {

if (flag == 1) {

// 先吃面包再喝水,环路等待条件

synchronized (bread) {

try {

Thread.sleep(500);

} catch (Exception e) {

e.printStackTrace();

}

System.out.println(“面包吃了,等喝水。。。”);

}

synchronized (water) {

System.out.println(“面包吃了,水也喝到了”);

}

}

if (flag == 0) {

// 先喝水再吃面包,环路等待条件

synchronized (water) {

try {

Thread.sleep(500);

} catch (Exception e) {

e.printStackTrace();

}

System.out.println(“喝完水,等吃面包了。。。”);

}

synchronized (bread) {

System.out.println(“喝完水,面包也吃完了。。。”);

}

}

}

程序运行结果:

喝完水,等吃面包了。。。

面包吃了,等喝水。。。

面包吃了,水也喝到了

喝完水,面包也吃完了。。。

这样就可以消除死锁发生~~~

活锁示例


import java.lang.management.ManagementFactory;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.TimeUnit;

import java.util.concurrent.locks.ReentrantLock;

/** @author Strive */

public class LiveLock {

static ReentrantLock bread = new ReentrantLock();

static ReentrantLock water = new ReentrantLock();

public static void main(String[] args) {

String name = ManagementFactory.getRuntimeMXBean().getName();

String pid = name.split(“@”)[0];

System.out.println(“Pid is:” + pid);

System.out.println("jstack " + pid);

ExecutorService executorService = Executors.newCachedThreadPool();

executorService.submit(

() -> {

try {

// 尝试获得 bread 锁

while (bread.tryLock(10, TimeUnit.SECONDS)) {

System.out.println(“拿到面包了,等着拿水。。。”);

TimeUnit.SECONDS.sleep(1);

// 判断 water 是否被锁住,如果锁住,退出!再次尝试获取 water 锁

boolean locked = water.isLocked();

if (locked) {

bread.unlock();

continue;

}

// 尝试获得 water 锁

boolean w = water.tryLock(10, TimeUnit.SECONDS);

// 如果没有获取到 water 锁,释放 bread 锁,再次尝试!

if (!w) {

bread.unlock();

continue;

}

System.out.println(“拿到水了”);

break;

}

System.out.println(“吃面包,喝水!”);

} catch (InterruptedException e) {

System.out.println(“线程中断”);

} finally {

water.unlock();

bread.unlock();

}

});

executorService.submit(

() -> {

try {

while (water.tryLock(10, TimeUnit.SECONDS)) {

System.out.println(“拿到水了,等着拿面包。。。”);

TimeUnit.SECONDS.sleep(2);

// 判断 bread 是否被锁住,如果锁住,退出!再次尝试获取 bread 锁

boolean locked = bread.isLocked();

if (locked) {

water.unlock();

continue;

}

// 尝试获得 bread 锁

boolean b = bread.tryLock(10, TimeUnit.SECONDS);

// 如果没有获取到 bread 锁,释放 water 锁,再次尝试!

if (!b) {

water.unlock();

最后

由于篇幅原因,就不多做展示了

boolean locked = bread.isLocked();

if (locked) {

water.unlock();

continue;

}

// 尝试获得 bread 锁

boolean b = bread.tryLock(10, TimeUnit.SECONDS);

// 如果没有获取到 bread 锁,释放 water 锁,再次尝试!

if (!b) {

water.unlock();

最后

[外链图片转存中…(img-tml6Y7K1-1719265657219)]

[外链图片转存中…(img-6yr9Lggx-1719265657220)]

[外链图片转存中…(img-wOfNLM4U-1719265657220)]

[外链图片转存中…(img-XZyEg74T-1719265657221)]

[外链图片转存中…(img-JolaYjTw-1719265657223)]

[外链图片转存中…(img-eJWvYp02-1719265657223)]

由于篇幅原因,就不多做展示了

  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值