什么是死锁?

Posted okokabcd

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了什么是死锁?相关的知识,希望对你有一定的参考价值。

问题

什么是死锁?请模拟写出一段 Java 死锁的核心代码?如何避免死锁?

答案

什么是死锁?

有一张银行卡,小A想往里存钱,小B想取钱,存钱和取钱需要卡和密码,现在小A有卡不知道密码,小B知道密码但是没有卡,陷入无限等待状态,这就是死锁。可用jstack命令进行分析。

死锁代码

public class ThreadDeadlock {

    public static void main(String[] args) throws InterruptedException {
        Integer obj1 = new Integer(1);
        Integer obj2 = new Integer(2);
        Integer obj3 = new Integer(3);

        Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1");
        Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2");
        Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3");

        t1.start();
        t2.start();
        t3.start();
    }

}

class SyncThread implements Runnable {
    private Object obj1;
    private Object obj2;

    public SyncThread(Object o1, Object o2) {
        this.obj1 = o1;
        this.obj2 = o2;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println(name + " acquiring lock on " + obj1);
        synchronized (obj1) {
            System.out.println(name + " acquired lock on " + obj1);
            work();
            System.out.println(name + " acquiring lock on " + obj2);
            synchronized (obj2) {
                System.out.println(name + " acquired lock on " + obj2);
                work();
            }
            System.out.println(name + " released lock on " + obj2);
        }
        System.out.println(name + " released lock on " + obj1);
        System.out.println(name + " finished execution.");
    }

    private void work() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

t3 acquiring lock on 3
t3 acquired lock on 3
t2 acquiring lock on 2
t1 acquiring lock on 1
t1 acquired lock on 1
t2 acquired lock on 2    
t2 acquiring lock on 3 # t2得到资源2的锁等待资源3的锁
t3 acquiring lock on 1 # t3得到资源3的锁等待资源1的锁
t1 acquiring lock on 2 # t1得到资源1的锁等待资源2的锁
# 至此3个线程相互等待进入死锁

如何避免死锁

  • 考虑加锁的顺序 每个人获取锁的顺序相同
  • 避免无限期的等待,考虑加锁的时限 每个人持有锁而未访问到资源的超时时间
  • 避免嵌套封锁
  • 只对有请求的进行封锁

通过编程发现Java死锁

DeadlockHandler.java

import java.lang.management.ThreadInfo;

public interface DeadlockHandler {
    void handleDeadlock(final ThreadInfo[] deadlockedThreads);
}

DeadlockDetector.java

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class DeadlockDetector {

    private final DeadlockHandler deadlockHandler;
    private final long period;
    private final TimeUnit unit;
    private final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    final Runnable deadlockCheck = new Runnable() {
        @Override
        public void run() {
            long[] deadlockedThreadIds = DeadlockDetector.this.mbean.findDeadlockedThreads();

            if (deadlockedThreadIds != null) {
                ThreadInfo[] threadInfos = DeadlockDetector.this.mbean.getThreadInfo(deadlockedThreadIds);

                DeadlockDetector.this.deadlockHandler.handleDeadlock(threadInfos);
            }
        }
    };

    public DeadlockDetector(final DeadlockHandler deadlockHandler, final long period, final TimeUnit unit) {
        this.deadlockHandler = deadlockHandler;
        this.period = period;
        this.unit = unit;
    }

    public void start() {
        this.scheduler.scheduleAtFixedRate(this.deadlockCheck, this.period, this.period, this.unit);
    }
}

DeadlockConsoleHandler.java

public class DeadlockConsoleHandler implements DeadlockHandler {

    @Override
    public void handleDeadlock(final ThreadInfo[] deadlockedThreads) {
        if (deadlockedThreads != null) {
            System.err.println("Deadlock detected!");
            Map<Thread, StackTraceElement[]> stackTraceMap = Thread.getAllStackTraces();
            for (ThreadInfo threadInfo : deadlockedThreads) {
                if (threadInfo != null) {
                    for (Thread thread : Thread.getAllStackTraces().keySet()) {
                        if (thread.getId() == threadInfo.getThreadId()) {
                            System.err.println(threadInfo.toString().trim());
                            for (StackTraceElement ste : thread.getStackTrace()) {
                                System.err.println("t" + ste.toString().trim());
                            }
                        }
                    }
                }
            }
        }
    }
}

使用:

// 在启动时启动一个周期性调用线程
DeadlockDetector deadlockDetector = new DeadlockDetector(new DeadlockConsoleHandler(), 5, TimeUnit.SECONDS);
deadlockDetector.start();

技术分享图片

参考

以上是关于什么是死锁?的主要内容,如果未能解决你的问题,请参考以下文章

一篇文章搞清JVM死锁问题及排查

Java并发编程实战 04死锁了怎么办?

Java并发编程实战 04死锁了怎么办?

面试官:手写一个必然死锁的例子?一顿操作猛如虎。。

java 死锁

死锁的产生原因以及解决方案