微信搜索:小大白日志

聊聊死锁

点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人。

文章不定期同步公众号,还有各种一线大厂面试原题、我的学习系列笔记。

死锁的四个必要条件

  • 互斥:一个资源不能同时被多个进程共享
  • 请求与保持:一个进程若已经获得了部分资源,还在请求另一个资源时,不会释放已请求到的资源
  • 不可剥夺:不能剥夺进程未使用完的资源,只能自己使用完后释放
  • 循环等待:多个进程互相循环等待对方的资源
    image

如何避免死锁

四个条件逐一破坏

  • 破坏“互斥”:资源的互斥一般是不可以被破坏的,虽有主要破坏其他三个条件
  • 破坏“请求与保持”:进程必须一次性申请万所有资源才能运行
  • 破坏“不可剥夺”:进程拥有未使用完资源,又同时申请其他资源而失败时,必须释放这些未使用完的资源,待到下次要使用时再重新申请
  • 破坏“循环等待”:给所有资源编号,进程申请资源时必须按序申请

如何检测死锁

先运行一段死锁程序:
public class ThreadTest {

    public static void main(String[] args) {
        testLockedThread(); //模拟并检测死锁
    }
    
    public static void testLockedThread(){
            //第一个线程
            new Thread(()->{
                synchronized(Integer.class){
                    try {
                        Thread.sleep(1000);
                        System.out.println("线程1开始获取String锁...");
                        synchronized (String.class){
                            System.out.println("线程1获取String锁成功!");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            //第二个线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized(String.class){
                        try {
                            Thread.sleep(1000);
                            System.out.println("线程2获取Integer锁...");
                            synchronized (Integer.class){
                           System.out.println("线程2获取Integer锁成功!");
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            try {
                Thread.sleep(100000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }
}

运行结果:

image

  • 使用JConsole工具

    在JAVA_HOME/bin目录下有一个虚拟机监控工具JConsole,双击运行后选择一个想要监控的进程,这里选择刚刚启动的发生死锁的进程:
    image

进入后选择“线程”->“检测死锁”,就可以查看刚刚发生死锁的这个线程信息

Thread-0:
image

Thread-1:
image

可知,在线程Thread-0中,它申请java.lang.Class@6d6f6e28这个资源,但这个资源已被Thread-1占用;同理,在线程Thread-1中,它申请java.lang.Class@72ea2f77这个资源,当这个资源已被Thread-0占用,故发生了死锁

  • 使用jdk自带的命令行工具Jstack
  • 使用jdk1.8的接口ThreadMXBean

乐观锁的实现原理

版本号机制+递归

如果数据库中的确发生了死锁,应该怎么解决

分多种情况:

  • 事务间对资源交替访问:
    如用户A锁住了表A又去访问表B,同时用户B锁住了表B又去访问表A,就造成了A、B互相等待对方的锁而发生死锁 ;只能通过检查程序代码解决
  • 并发修改同一条记录:如A拿到了独占锁正准备修改一条记录->同时B进来拿到共享锁读该条记录,读完后正准备修改该条记录->A想修改这条记录就必须让B先释放共享锁(A等B),B想修改就必须让A释放独占锁(B等A),这样就造成了死循环;解决方法:使用乐观锁(版本号机制+递归)、悲观锁(每次都加锁)
  • 索引失效引发死锁
    索引也是一种资源,当多个事务对资源进行争夺、互相等待时就会引发死锁

OK,如果文章哪里有错误或不足,欢迎各位留言。
创作不易,各位的「三连」是二少创作的最大动力!我们下期见!

posted @ 2021-06-20 00:57  明天喝可乐  阅读(57)  评论(0)    收藏  举报