美文网首页
Java死锁

Java死锁

作者: Android_Gleam | 来源:发表于2020-09-27 14:48 被阅读0次

了解死锁之前,我们要先了解线程的状态,或者叫线程的生命周期。

线程的状态

线程的状态主要分为上图中的这几种状态,这里我们需要注意一下几点:

  • 初始状态
    new出的一个线程对象,注意此时线程并未执行,只有调用start方法后才会执行。
  • 运行态
    Java中规定将两种合二为一 操作系统分为两种:
    • 运行中 -->被Cpu分配了时间片
    • 就绪 -->等待被Cpu分配了时间片
  • 阻塞态
    有且仅有调用synchronized关键字且没有拿到锁的情况下算阻塞 wait、sleep等不算,算等待或等待超时
    lock显示锁 没有拿到锁 进入等待或等待超时状态,因为Lock底层调用的是LockSupport
    阻塞被动进入 等待主动进入

定义

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。

造成死锁的原因及解决办法

造成死锁需要以下三个条件,下面我们对这三个条件,进行详细的分析和解释。

  • 多个操作者 (M>=2) 争夺多个资源(N>=2) N<=M

为什么需要多个操作者,如果只有一个操作者,还需要加锁吗? 很显然不需要
为什么需要多个资源,如果只有一个资源,谁抢到就是谁的,用完了其他人接着用。
为什么需要N<=M,如果资源数大于操作者数量,操作者可以去争抢没有被争抢的资源。

  • 争夺资源顺序不对

public class DieSync {

    private static Object objectA = new Object();
    private static Object objectB = new Object();

    private static void MainDo() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (objectA){
            System.out.println(threadName + "get objectA");
            Thread.sleep(100);
            synchronized (objectB){
                System.out.println(threadName + "get objectB");
            }
        }

    }

    private static void SonDo() throws InterruptedException {
        String threadName = Thread.currentThread().getName();
        synchronized (objectB){
            System.out.println(threadName + "get objectB");
            Thread.sleep(100);
            synchronized (objectA){
                System.out.println(threadName + "get objectA");
            }
        }
    }

    private static class SonThread extends Thread{
        private String name;

        public SonThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            Thread.currentThread().setName(name);
            try {
                SonDo();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread.currentThread().setName("主线程");
        SonThread sonThread = new SonThread("子线程");
        sonThread.start();
        MainDo();
    }

}

主线程先拿到了A,然后去拿B,发现B被子线程拿了,然后就在这阻塞着
而子线程先拿到了B,然后去拿A,这时候发现A被主线程拿了,然后也阻塞
两者互不相让,你等我,我等你,等到天荒地老,海枯石烂
如果我们将代码改为,主线程和子线程都先拿A在拿B,这个问题就解决了,这也就是解决办法,代码就不演示了,大家可以自行尝试。

  • 拿到资源不放手

这点很好理解,就是说你拿到锁了,一直不撒手,占着茅坑不出来了,外面的人自然就拉了裤了,哈哈。
我们下面通过段代码来演示下正确的做法。

public class TryLock {
    private static Lock lockA = new ReentrantLock();
    private static Lock lockB = new ReentrantLock();

    private static void firstToSecond() throws InterruptedException {
        String threadNmae = Thread.currentThread().getName();
        Random random = new Random();
        while (true) {
            if (lockA.tryLock()) {
                try {
                    System.out.println(threadNmae + "get lockA");
                    if (lockB.tryLock()) {
                        try {
                            System.out.println(threadNmae + "get lockB");
                            System.out.println("firstToSecond do work.....");
                            break;
                        } finally {
                            lockB.unlock();
                        }
                    }
                } finally {
                    lockA.unlock();
                }
            }
            Thread.sleep(random.nextInt(3));
        }
    }


    private static void secondToFirst() throws InterruptedException {
        String threadNmae = Thread.currentThread().getName();
        Random random = new Random();
        while (true) {
            if (lockB.tryLock()) {
                try {
                    System.out.println(threadNmae + "get lockB");
                    if (lockA.tryLock()) {
                        try {
                            System.out.println(threadNmae + "get lockA");
                            System.out.println("secondToFirst do work.....");
                            break;
                        } finally {
                            lockA.unlock();
                        }
                    }
                } finally {
                    lockB.unlock();
                }
            }
            Thread.sleep(random.nextInt(3));
        }
    }

    private static class SecondThread extends Thread{
        private String name;

        public SecondThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            Thread.currentThread().setName(name);
            try {
                secondToFirst();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread.currentThread().setName("主线程");
        SecondThread secondThread = new SecondThread("子线程");
        secondThread.start();
        firstToSecond();
    }
}

这段代码的逻辑也很简单,就是将内置锁换成了显示锁,显示锁(Lock)可以进行尝试拿锁,中断拿锁等操作。先尝试拿锁,拿到了继续执行,没拿到就继续循环,直到拿到为止。
这里要注意以下两点:

  • 当我们使用Lock的时候,一定要记得加try{} finally{}结构,在finally中释放锁,避免我们代码抛出异常,导致锁不释放,造成死锁。
  • 我想大家都注意到了这行代码 Thread.sleep(random.nextInt(3));,这行代码的作用就是避免一直尝试拿锁,造成资源浪费,这种现象也叫活锁。打印结果太长了,大家有兴趣可以自己运行代码看一下。

总结

上面我们说造成死锁有三个条件,条件1的造成原因是因为我们的业务需要,没有办法解决,所以我们只能从后两个条件想办法,具体的解决办法,上面代码也给出了示例,主要方法就是

  • 调整拿锁顺序
  • 用显示锁,尝试去拿锁(注意我们上面提到的注意点)

相关文章

  • Java死锁检测之ThreadMXBean

    看此文章前请先了解之前一篇文章 "Java死锁之理解死锁" 中的死锁示例java 中提供了可以检测死锁的工具类Th...

  • Java concurrency《防止死锁》

    Java concurrency《防止死锁》 常见预防死锁的办法 有顺序的锁 具有超时时间的锁 死锁的检测 有顺序...

  • 如何去检测死锁

    如何检测死锁 死锁预防 让线程获取锁的顺序一致 死锁检测 jps 查看java 进程信息 jstack +进程号 ...

  • 死锁

    在JAVA编程中,有3种典型的死锁类型: 静态的锁顺序死锁 动态的锁顺序死锁 协作对象之间发生的死锁 静态的锁顺序...

  • Java死锁的简单例子

    Java死锁的简单例子 两个线程互相占有对方需要的资源而不释放,便形成了死锁。 代码如下:Program.java...

  • JVM_JMM: 死锁的检测

    死锁的示例代码: 通过jconsole来检测死锁: 名称: Thread-1状态: java.lang.Class...

  • java死锁介绍、源码实现及预防(含源码)

    java死锁介绍、源码实现及预防(含源码) 什么是死锁 死锁是 多个线程 之间 相互之间 持有 对方需要的资源,同...

  • Java高并发 -- 并发扩展

    Java高并发 -- 并发扩展 主要是学习慕课网实战视频《Java并发编程入门与高并发面试》的笔记 死锁 死锁是指...

  • Java死锁检测方式JConsole

    Java死锁检测方式之JConsole 我们在开发中应该尽量避免死锁,但是如果真的有死锁产生那么我们怎么在一个复杂...

  • Java死锁

    什么是死锁 死锁检测 产生死锁的四个必要条件 如何避免死锁 死锁 死锁,指两个或多个线程之间,由于互相持有对方需要...

网友评论

      本文标题:Java死锁

      本文链接:https://www.haomeiwen.com/subject/squpektx.html