让我们来聊一聊”死锁“

在多线程的世界里,一个不小心可能就会造成”死锁“,一旦”死锁“了意味着CPU会一直在空转,结果就是CPU一下子飙到100%,所以避免死锁的前提,就要去聊聊什么是”死锁“以及如何去避免死锁。

什么是死锁?

死锁指的是,两个或者多个线程,持有了彼此请求的资源,导致线程之间互相在等待对方释放资源,所以造成了死锁,下面我们来手撸一个简单的死锁例子。

public class DeadLock {

    public static void main(String[] args) {

        Object lock1 = new Object();
n
        Object lock2 = new Object();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock1) {
                    System.out.println("thread get lock1");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lock2) {
                        System.out.println("thread get lock2");
                    }
                }
            }
        });

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock2) {
                    System.out.println("thread1 get lock2");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (lock1) {
                        System.out.println("thread1 get lock1");
                    }
                }
            }
        });

        thread.start();
        thread1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread's status:"+thread.getState());
        System.out.println("thread1's status:"+thread1.getState());
    }
}

打印的结果如下
在这里插入图片描述
可以看到线程thread先拿到了锁lock1,然后工作了一会之后,想要获取锁lock2,但是此时锁lock2被线程thread1占有着,而线程thread1同时也在请求获取被线程thread占有的锁lock,所以从打印信息可以看出,两个线程都处于阻塞等待状态。


造成死锁的必要条件

造成死锁必须满足四个必要条件,如下表所示。

条件描述
资源互斥性被共享的同步资源每次只能被一个线程占有
资源不可抢占性资源请求方不可以抢占资源拥有方占有的资源
请求和占有资源请求方在请求资源的同时,也占有着原来拥有的资源
循环等待多个线程之间相互等待对方释放资源,造成循环等待

检测死锁的方法

一般遇到死锁,时间久了,CPU可能会飙到100%,所以可以通过top命令查看cpu过高的进程,或者直接通过命令jps查看java进程id号,然后通过命令jstack - id来查看进程的堆栈情况,搜索一下DeadLock关键字就可以确定死锁发生的位置。

在这里插入图片描述

死锁的避免

只要破坏死锁的四个必要条件中的一个,就可以避免死锁了。对于”资源互斥性“条件是很难被破坏的,因为资源互斥是保证线程安全的前提条件。

使用Lock来破坏“请求和占有”和“资源不可抢占”条件

可以使用JUC包下的ReentrantLock类来作为锁对象,这样线程在等待指定时间后,还未获取到锁对象,就会返回失败状态,然后在finally中主动去释放掉自己占有的锁对象,这样就破坏了“请求和占有”和“资源不可抢占”两个条件了,代码如下。

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @program: tengxun
 * @description: 死锁
 * @author: cjr
 * @create: 2020-06-29 22:00
 **/
public class DeadLock {

    public static void main(String[] args) {

        Lock lock1 = new ReentrantLock();

        Lock lock2 = new ReentrantLock();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if (lock1.tryLock(2000, TimeUnit.MICROSECONDS)) {
                        System.out.println("thread get lock1");
                        Thread.sleep(50);
                        if (lock2.tryLock(2000,TimeUnit.MICROSECONDS)) {
                            System.out.println("thread get lock2");
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    if (lock2.tryLock(2000, TimeUnit.MICROSECONDS)) {
                        System.out.println("thread1 get lock2");
                        Thread.sleep(50);
                        if (lock1.tryLock(2000,TimeUnit.MICROSECONDS)) {
                            System.out.println("thread1 get lock1");
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread.start();
        thread1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread's status:"+thread.getState());
        System.out.println("thread1's status:"+thread1.getState());

    }
}

输出的结果如下
在这里插入图片描述

规定好获取锁的顺序,这样就可以破坏"循环等待"条件

之所以会循环等待,就是因为获取锁的顺序,线程A先获取锁对象1,然后获取锁对象2,线程B先获取锁对象2,然后获取锁对象1,所以造成循环等待对方释放锁资源,造成死锁,我们可以根据对象的hashcode的大小来决定获取锁的顺序,代码如下

public class DeadLock {



    public static void main(String[] args) {

        Object lock1 = new Object();

        Object lock2 = new Object();

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Object left = null;
                Object right = null;
                left = lock1.hashCode() > lock2.hashCode() ? lock1 : lock2;
                right = lock1.hashCode() > lock2.hashCode() ? lock2 : lock1;
                synchronized (left) {
                    System.out.println("thread get lock1");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (right) {
                        System.out.println("thread get lock2");
                    }
                }
            }
        });

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Object left = null;
                Object right = null;
                left = lock1.hashCode() > lock2.hashCode() ? lock1 : lock2;
                right = lock1.hashCode() > lock2.hashCode() ? lock2 : lock1;
                synchronized (left) {
                    System.out.println("thread1 get lock2");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (right) {
                        System.out.println("thread1 get lock1");
                    }
                }
            }
        });

        thread.start();
        thread1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("thread's status:"+thread.getState());
        System.out.println("thread1's status:"+thread1.getState());

    }
}

输出结果如下
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值