基于嵌套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

基于嵌套Synchronized实现DeadLock_第1张图片

2.2 使用Jstack指令追踪死锁

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

# jstack语法规则
jstack -l -e

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

可以看到线程池中的两个线程分别持有对方申请的资源:
基于嵌套Synchronized实现DeadLock_第2张图片

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

3.1 结束死锁进程

# kill指令语法规则
kill -9

# 执行kill指令
kill -9 86989

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

你可能感兴趣的:(java,jvm,面试)