死锁的认识

文章目录

      • 一.死锁的三个典型情况
      • 二.死锁产生的四个必要条件
      • 三.如何破除死锁


一.死锁的三个典型情况

1.一个线程对同一对象连续加锁两次,如果锁是不可重入的就会造成死锁
什么是可重入锁 点击

2.两个线程两把锁,t1和t2各自先针对锁A和锁B加锁,再互相获取对方的锁

例子:线程 t1 先请求对 apple 加锁,t2 对 banana 加锁,之后 t1 线程在持有apple的情况请求对 banana 加锁,t2线程在持有 banana 的情况请求对 apple 加锁,造成两个线程互相等待。

	public class demo2 {
    public static void main(String[] args) {
        Object apple = new Object();
        Object banana = new Object();
        Thread t1 = new Thread(()->{
           synchronized (apple){
               try {
                   Thread.sleep(1000); //加 sleep 为了确保让两个线程把第一把锁拿到,否则不容易构建出这个情况因为线程抢占式执行的
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               synchronized (banana){
                   System.out.println("t1拿到苹果和香蕉了");
               }
           }
        },"t1");
        Thread t2 = new Thread(()->{
            synchronized (banana){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (apple){
                    System.out.println("t2拿到苹果和香蕉了");
                }
            }
        },"t2");
        t1.start();
        t2.start();
    }
}

程序输出为空:
死锁的认识_第1张图片

通过 jconsole 工具查看线程情况:
t1:
死锁的认识_第2张图片
t2:
死锁的认识_第3张图片


3.多个线程多把锁
例子:哲学家就餐问题
如下图每两个人之间有一只筷子,每个人只能拿左右手边的筷子,只有拿够两只筷子才能吃饭。
如果出现极端情况每个人都拿了一只,就会使所有人不能吃饭互相等待,造成死锁。
死锁的认识_第4张图片


二.死锁产生的四个必要条件

互斥使用 :对同一个对象上锁,多个线程中只有一个能成功,其他线程阻塞
不可强占:一但一个线程拿到锁,其他线程只能等其释放,不能抢
请求和保持 :一个线程拿到锁A,在没有释放锁A的基础上请求获取锁B。
循环等待:两个线程两把锁,t1和t2各自先针对锁A和锁B加锁,再互相获取对方的锁


三.如何破除死锁

只需让死锁产生的四个条件有一个不满足即可,前三个是锁的特性,以前OS就是这样设计的,对于synchronized 前三个条件修改不了。从第四个条件入手:如采用给锁编号,指定加锁顺序的方法

如上例,当我们给锁对象(筷子)编号,指定加锁顺序(这里采用从小到大的顺序):
下图,AE会争抢1号筷子,其他三人都去拿其右手边筷子。如果E拿到1,E会和D有一个成功拿到5,其它四人陷入阻塞,成功的人就餐结束后释放锁(筷子),最后每个人都能吃到饭;同理如果A拿到1后,D会再拿到5先吃到饭(E会阻塞因为拿锁顺序是从小到大只能拿1),最后每个人都能吃到饭。
死锁的认识_第5张图片

你可能感兴趣的:(Java,JUC,java,jvm,开发语言)