死锁编码及定位分析

52 阅读2分钟

一、什么是死锁?

    死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去。如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低。否则就会因争夺有限的资源而陷入死锁。

    或者说,如果一组进程中的每一个进程都在等待仅由该组进程中的其它进程才能引发的事件,那么该组进程是死锁的。

二、产生死锁的必要条件

    虽然进程在运行过程中可能会发生死锁,但产生进程死锁是必须具备一定条件的。所以,要产生死锁必须同时具备下面四个必要条件,只要其中任一个条件不成立,死锁就不会发生。

2.1 互斥条件

    进程对所分配到的资源进行排它性使用,即在一段时间内,某资源只能被一个进程占用。如果此时还有其他进程请求该资源,则请求线程只能等待,直至占有该资源的进程用完释放。

2.2 请求和保持条件

    进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求线程被阻塞,但对自己已获得的资源保持不放。

2.3 不可抢占条件

    进程已获得的资源在未使用完之前不能被抢占,只能在进程使用完时由自己释放。

2.4 循环等待条件

    在发生死锁时,必然存在一个进程——资源的循环链,即进程集合{P0,P1,P2,…,Pn}中的 P0 正在等待一个 P1 占用的资源,P1正在等待 P2 占用的资源,…,Pn 正在等待已被 P0 占用的资源。

三、死锁演示

class HoldLockDemo implements Runnable {

    private String lockA;
    private String lockB;

    public HoldLockDemo(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA) {
            System.out.println(Thread.currentThread().getName() + "\t 自己持有" + lockA + "\t 尝试获得" + lockB);

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + "\t 自己持有" + lockB + "\t 尝试获得" + lockA);
            }
        }
    }
}

public class DeadLockDemo {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new HoldLockDemo(lockA, lockB), "Thread-A").start();
        new Thread(new HoldLockDemo(lockB, lockA), "Thread-B").start();
    }
}

image.png

四、使用 jps 和 jstack 检查

4.1 使用 jps 查看当前的 java 进程

image.png

4.2 使用 jstack 查看指定进程的堆栈信息

image.png