基于嵌套Synchronized实现DeadLock

一、设计一个死锁场景

- 从线程池中取出2条线程:A、B
- 声明a、b变量
- Thread-A负责锁定a变量,持有不释放;且申请b变量
- Thread-B负责锁定b变量,持有不释放;且申请a变量
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * 死锁发生的4个必要条件
 * 1、mutual exclusion:资源互斥
 * 2、hold and wait   :线程持有 ≥1 个资源不释放,并且另外请求 ≥1 个资源
 * 3、no preemption   :无抢占,除非线程主动释放资源
 * 4、circular wait   :循环等待,基于第3点解释,线程不抢占资源就需要在队列中等待资源释放;下方描述的synchronized锁是非公平锁,线程不遵从FIFO队列原则;
 *
 */
@Slf4j
public class DeadlockDemo {

    static AtomicInteger resourceA = new AtomicInteger(1);
    static AtomicInteger resourceB = new AtomicInteger(1);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService exec = Executors.newFixedThreadPool(2);
        exec.execute(() -> {
            synchronized (resourceA) {
                resourceA.getAndDecrement();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                log.warn("threadName={}, waiting for lock resourceB... ", Thread.currentThread());
                synchronized (resourceB) {
                    resourceB.getAndDecrement();
                    int aNow = resourceA.get();
                    int bNow = resourceB.get();
                    log.warn("threadName={}, a={}, b={}", Thread.currentThread(), aNow, bNow);
                }
            }
        });

        exec.execute(() -> {
            synchronized (resourceB) {
                resourceB.getAndDecrement();
                try {
                    Thread.sleep(2000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                log.warn("threadName={}, waiting for lock resourceA... ", Thread.currentThread());
                synchronized (resourceA) {
                    resourceA.getAndDecrement();
                    int aNow = resourceA.get();
                    int bNow = resourceB.get();
                    log.warn("threadName={}, a={}, b={}", Thread.currentThread(), aNow, bNow);
                }
            }
        });
    }
}

二、分析死锁状态

2.1 查找Java程序对应pid

在这里插入图片描述

2.2 使用Jstack指令追踪死锁

在控制台中输入以下指令:

# jstack语法规则
jstack -l -e <pid>

# 执行jstack指令
jstack -l -e 86989

可以看到线程池中的两个线程分别持有对方申请的资源:
在这里插入图片描述

三、使用“外力”中断死锁

3.1 结束死锁进程

# kill指令语法规则
kill -9 <pid>

# 执行kill指令
kill -9 86989

注意:在生产环境中执行kill指令结束死锁进程只是临时解决方案,需要立即排查具体业务情况以对死锁发生的4个条件进行破坏,令线程无法进入死锁状态。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值