死锁和Java内存区域


死锁

概念

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

如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

下面通过一个代码来实现上图的情况以模拟死锁:

import java.util.concurrent.TimeUnit;

/**
 * 资源类
 */
class HoldResourceThread implements Runnable{

    private String resource1;
    private String resource2;

    public HoldResourceThread(String resource1, String resource2) {
        this.resource1 = resource1;
        this.resource2 = resource2;
    }

    @Override        // 持有自己的资源,还想得到别人的资源
    public void run() {
        synchronized (resource1) {
            System.out.println(Thread.currentThread().getName() + "\t 自己持有" + resource1 + "\t 尝试获取:" + resource2);

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

            synchronized (resource2) {
                System.out.println(Thread.currentThread().getName() + "\t get" + resource2);
            }
        }
    }
}
public class DeadLockDemo {
    public static void main(String[] args) {
        String resource1 = "resource1";
        String resource2 = "resource2";

        new Thread(new HoldResourceThread(resource1, resource2), "A").start();

        new Thread(new HoldResourceThread(resource2, resource1), "B").start();
    }
}

运行结果如下,main线程无法结束

A	 自己持有resource1	 尝试获取:resource2
B	 自己持有resource2	 尝试获取:resource1

线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后让线程 A 休眠 2s 为的是让线程 B 得到执行然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁

上面的例子符合产生死锁的四个必要条件:

  • 互斥条件:该资源任意时刻只由一个线程占用
  • 请求于保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
  • 不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只能自己使用完毕后才释放资源
  • 循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系

预防和避免死锁

如何预防死锁? 破坏死锁的产生的必要条件即可:

  1. 破坏请求与保持条件 :一次性申请所有的资源
  2. 破坏不剥夺条件 :占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源
  3. 破坏循环等待条件 :靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件

如何避免死锁?避免死锁就是在资源分配时,借助于算法(比如银行家算法)对资源分配进行计算评估,使其进入安全状态。

安全状态 指的是系统能够按照某种线程推进顺序(P1、P2、P3…..Pn)来为每个线程分配所需资源,直到满足每个线程对资源的最大需求,使每个线程都可顺利完成。称 <P1、P2、P3.....Pn> 序列为安全序列

排查死锁

当我们出现死锁的时候,首先需要使用 jps 命令查看运行的程序

jps -l

我们可以看到 DeadLockDemo 这个类,一直在运行

再使用 jstack 查看堆栈信息,后面参数是 jps 输出该类的 pid

jstack 23504

输出结果

通过查看最后一行,我们看到 Found 1 deadlock,即存在一个死锁

Java内存区域

这里推荐看这篇文章: Java 内存区域详解(重点)


文章作者: 不才叶某
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 不才叶某 !
评论
  目录