精华内容
下载资源
问答
  • https://www.cnblogs.com/ysyy/archive/2019/05/22/10904081.html
    展开全文
  • 一、导致线程死锁的原因 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。这是从...

    欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

    一、导致线程死锁的原因

    多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。这是从网上其他文档看到的死锁产生的四个必要条件:

    1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用。
    2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
    3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
    4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

    当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。下面用java代码来模拟一下死锁的产生。

    模拟两个资源:

    class ThreadResource {
        public static Object resource1 = new Object();
        public static Object resource2 = new Object();
    }
    

    模拟线程1占用资源1并申请获得资源2的锁:

    class Thread1 implements Runnable {
    
        @Override
        public void run() {
            try {
                System.out.println("Thread1 is running");
                synchronized (ThreadResource.resource1) {
                    System.out.println("Thread1 lock resource1");
                    Thread.sleep(2000);//休眠2s等待线程2锁定资源2
                    synchronized (ThreadResource.resource2) {
                        System.out.println("Thread1 lock resource2");
                    }
                    System.out.println("Thread1 release resource2");
                }
                System.out.println("Thread1 release resource1");
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            System.out.println("Thread1 is stop");
        }
    }
    

    模拟线程2占用资源2并申请获得资源1的锁:

    class Thread2 implements Runnable {
    
        @Override
        public void run() {
            try {
                System.out.println("Thread2 is running");
                synchronized (ThreadResource.resource2) {
                    System.out.println("Thread2 lock resource2");
                    Thread.sleep(2000);//休眠2s等待线程1锁定资源1
                    synchronized (ThreadResource.resource1) {
                        System.out.println("Thread2 lock resource1");
                    }
                    System.out.println("Thread2 release resource1");
                }
                System.out.println("Thread2 release resource2");
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            System.out.println("Thread2 is stop");
        }
    }
    

    同时运行俩个线程:

    public class ThreadTest {
    
        public static void main(String[] args) {
            new Thread(new Thread1()).start();
            new Thread(new Thread2()).start();
        }
    
    }
    

    完整代码:

    package com.concurrent.deadLock;
    
    /**
     * @author riemann
     * @date 2019/08/14 22:44
     */
    public class ThreadTest {
    
        public static void main(String[] args) {
            new Thread(new Thread1()).start();
            new Thread(new Thread2()).start();
        }
    
    }
    
    class ThreadResource {
        public static Object resource1 = new Object();
        public static Object resource2 = new Object();
    }
    
    class Thread1 implements Runnable {
    
        @Override
        public void run() {
            try {
                System.out.println("Thread1 is running");
                synchronized (ThreadResource.resource1) {
                    System.out.println("Thread1 lock resource1");
                    Thread.sleep(2000);//休眠2s等待线程2锁定资源2
                    synchronized (ThreadResource.resource2) {
                        System.out.println("Thread1 lock resource2");
                    }
                    System.out.println("Thread1 release resource2");
                }
                System.out.println("Thread1 release resource1");
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            System.out.println("Thread1 is stop");
        }
    }
    
    class Thread2 implements Runnable {
    
        @Override
        public void run() {
            try {
                System.out.println("Thread2 is running");
                synchronized (ThreadResource.resource2) {
                    System.out.println("Thread2 lock resource2");
                    Thread.sleep(2000);//休眠2s等待线程1锁定资源1
                    synchronized (ThreadResource.resource1) {
                        System.out.println("Thread2 lock resource1");
                    }
                    System.out.println("Thread2 release resource1");
                }
                System.out.println("Thread2 release resource2");
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            System.out.println("Thread2 is stop");
        }
    }
    

    输出结果:

    Thread1 is running
    Thread1 lock resource1
    Thread2 is running
    Thread2 lock resource2
    

    并且程序一直无法结束。这就是由于线程1占用了资源1,此时线程2已经占用资源2,。这个时候线程1想要使用资源2,线程2想要使用资源1。两个线程都无法让步,导致程序死锁。

    二、怎么解除线程死锁

    由上面的例子可以看出当线程在同步某个对象里,再去锁定另外一个对象的话,就和容易发生死锁的情况。最好是线程每次只锁定一个对象并且在锁定该对象的过程中不再去锁定其他的对象,这样就不会导致死锁了。比如将以上的线程改成下面这种写法就可以避免死锁:

    public void run()
    {
        try
        {
            System.out.println("Thread1 is running");
            synchronized (ThreadResource.resource1)
            {
                System.out.println("Thread1 lock resource1");
                Thread.sleep(2000);//休眠2s等待线程2锁定资源2
            }
            System.out.println("Thread1 release resource1");
            synchronized (ThreadResource.resource2)
            {
                System.out.println("Thread1 lock resource2");
            }
            System.out.println("Thread1 release resource2");
        }
        catch (Exception e)
        {
            System.out.println(e.getMessage());
        }
        System.out.println("Thread1 is stop");
    }
    

    但是有的时候业务需要同时去锁定两个对象,比如转账业务:A给B转账,需要同时锁定A、B两个账户。如果A、B相互同时转账的话就会出现死锁的情况。这时可以定义一个规则:锁定账户先后的规则。根据账户的某一个属性(比如id或者hasCode),判断锁定的先后。即每一次转账业务都是先锁定A再锁定B(或者先锁定B在锁定A),这样也不会导致死锁的发生。比如按照上面的例子,需要同时锁定两个资源,可以根据资源的hashcode值大小来判断先后锁定顺序。可以这样改造线程:

    class Thread3 implements Runnable {
    
        @Override
        public void run() {
            try {
                System.out.println("Thread is running");
                if (ThreadResource.resource1.hashCode() > ThreadResource.resource2.hashCode()) {
                    //先锁定resource1
                    synchronized (ThreadResource.resource1) {
                        System.out.println("Thread lock resource1");
                        Thread.sleep(2000);
                        synchronized (ThreadResource.resource2)
                        {
                            System.out.println("Thread lock resource2");
                        }
                        System.out.println("Thread release resource2");
                    }
                    System.out.println("Thread release resource1");
                } else {
                    //先锁定resource2
                    synchronized (ThreadResource.resource2)
                    {
                        System.out.println("Thread lock resource2");
                        Thread.sleep(2000);
                        synchronized (ThreadResource.resource1)
                        {
                            System.out.println("Thread lock resource1");
                        }
                        System.out.println("Thread release resource1");
                    }
                    System.out.println("Thread release resource2");
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
            }
            System.out.println("Thread1 is stop");
        }
    }
    

    输出结果:

    Thread is running
    Thread lock resource2
    Thread lock resource1
    Thread release resource1
    Thread release resource2
    Thread1 is stop
    

    三、如何避免死锁

    在有些情况下死锁是可以避免的。三种用于避免死锁的技术:

    1、加锁顺序
    2、加锁时限
    3、死锁检测

    加锁顺序

    当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就容易发生。

    按照顺序加锁是一种有效的死锁预防机制。但是,这种方式需要事先知道所有可能会用到的锁,但总有些时候是无法预知的。

    加锁时限

    当一个线程在尝试获取锁的过程中超过了这个时限则该线程应该放弃对该锁进行请求。

    若一个线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试。这段随机的等待时间让其它线程有机会尝试获取相同的这些锁,并且让该应用在没有获得锁的时候可以继续运行。

    需要注意的是,由于存在锁的超时,所以我们不能认为这种场景就一定是出现了死锁。也可能是因为获得了锁的线程(导致其它线程超时)需要很长的时间去完成它的任务。

    此外,如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。如果只有两个线程,并且重试的超时时间设定为0到500毫秒之间,这种现象可能不会发生,但是如果是10个或20个线程情况就不同了。因为这些线程等待相等的重试时间的概率就高的多(或者非常接近以至于会出现问题)。

    死锁检测

    死锁检测是一个更好的死锁预防机制,它主要是针对那些不可能实现按序加锁并且锁超时也不可行的场景。

    每当一个线程获得了锁,会在线程和锁相关的数据结构中(map、graph等等)将其记下。除此之外,每当有线程请求锁,也需要记录在这个数据结构中。

    当一个线程请求锁失败时,这个线程可以遍历锁的关系图看看是否有死锁发生。

    那么当检测出死锁时,这些线程该做些什么呢?

    一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁(编者注:原因同超时类似,不能从根本上减轻竞争)。

    一个更好的方案是给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。

    总结:死锁常见于,线程在锁定对象还没释放时,又需要锁定另一个对象,并且此时该对象可能被另一个线程锁定。这种时候很容易导致死锁。因此在开发时需要慎重使用锁,尤其是需要注意尽量不要在锁里又加锁。

    展开全文
  • Java解决线程死锁三种方案

    万次阅读 2018-11-30 16:41:13
    什么是死锁? 死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们...

    什么是死锁?

    死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞状态。

    下面代码展示一下死锁

    package com.zhr.Thread;
    
    /**
     * @ Author     :zhenghaoran.
     * @ Date       :Created in 14:55 2018/11/30
     * @ Description:
     */
    public class DeadLockSample extends Thread{
        private String first;
        private String second;
        public DeadLockSample (String name,String first,String second){
            super(name);
            this.first = first;
            this.second = second;
        }
    
        public void run(){
            synchronized (first){
                System.out.println(this.getName() + " obtained:" + first);
    
                try {
                    Thread.sleep(1000L);
                    synchronized (second){
                        System.out.println(this.getName() +" obtained: " + second);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            String lockA = "lockA";
            String lockB = "lockB";
            DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
            DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
    
        }
    
    }
    结果:
    Thread1 obtained:lockA
    Thread2 obtained:lockB
    或者
    Thread2 obtained:lockB
    Thread1 obtained:lockA
    

    看了上面的代码大家可能会有疑问在代码中明明是先启动的Thread1线程啊,为什么还会有第二个结果变成了Threa2先运行,其实这是在操作系统层面中系统会进行线程调度,在Java中线程调度是抢占式的,主线程虽然先运行了Thread1.start(),但是并不代表就一定会先运行run()方法,在两个线程没有设置线程优先级的时候默认都是5级,现在分别启动线程1核线程2,此时操作系统会将处在就绪状态的变为运行运行状态,所以这个时候线程2要是先就绪,并且cpu从就绪的线程当中选择了线程2name就会线程2先执行。

    当然在线上如果写出了死锁我们应该怎么办呢?别急让我来给你介绍两个工具都是 JDK自带的 jstack 和 jConsole。下面演示jstack的用法

    • 首先运行刚才的程序,让其处于死锁的状态
    • 运行ps -ef | grep java 找到正在运行的java程序的pid
    • 然后进入到JAVA_HOME/bin下运行 ./jstack 10586(这个是我当前运行的pid)

    如下图:
    在这里插入图片描述
    可以看到Threa2正在处于BLOCKED的状态,而Thread1也是同样处于BLOCKED这就说明了发生了死锁。并且可以定位问题到在是在DeadLockSimple.java:24这个文件的第24行,怎么样很神奇吧。

    下面我们看一下使用jConsole如何检测运行的代码是否有死锁

    • 同样的操作步骤 运行程序
    • 在JAVA_HOME/bin 目录下 运行jConsole 选择到对应的PID 可以看到对应的类名。

    如下图
    在这里插入图片描述
    然后我们选择死锁就能看到了当前死锁的线程
    在这里插入图片描述

    还有重头戏也就是最牛的 在代码中就可以进行检测,定位死锁,使用Java提供的标准管理 API,ThreadMXBean 类,具体代码如下

    package com.zhr.Thread;
    
    import java.lang.management.ManagementFactory;
    import java.lang.management.ThreadInfo;
    import java.lang.management.ThreadMXBean;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @ Author     :zhenghaoran.
     * @ Date       :Created in 14:55 2018/11/30
     * @ Description:
     */
    public class DeadLockSample extends Thread{
        private String first;
        private String second;
        public DeadLockSample (String name,String first,String second){
            super(name);
            this.first = first;
            this.second = second;
        }
    
        public void run(){
            synchronized (first){
                System.out.println(this.getName() + " obtained:" + first);
    
                try {
                    Thread.sleep(1000L);
                    synchronized (second){
                        System.out.println(this.getName() +" obtained: " + second);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
    
            Runnable dlCheck = new Runnable() {
                public void run() {
                    long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
                    if(deadlockedThreads != null){
                        ThreadInfo[] threadInfos= threadMXBean.getThreadInfo(deadlockedThreads);
                        System.out.println("Detected deadlock threads:");
                        for (ThreadInfo threadInfo : threadInfos) {
                            System.out.println(threadInfo.getThreadName());
    
                        }
                    }
    
                }
            };
    
            ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
            //稍等 1 秒,然后每 2 秒进行一次死锁扫描
            scheduler.scheduleAtFixedRate(dlCheck,1L,2L, TimeUnit.SECONDS);
    
            String lockA = "lockA";
            String lockB = "lockB";
            DeadLockSample t1 = new DeadLockSample("Thread1",lockA,lockB);
            DeadLockSample t2 = new DeadLockSample("Thread2",lockB,lockA);
            t1.start();
            t2.start();
            t1.join();
            t2.join();
        }
    
    }
    
    
    展开全文
  • 产生死锁的条件有四个 互斥条件, ...那么线程死锁是因为多线程,访问共享资源,由于访问的顺序不当所造成的的。通常是一个线程锁定了一个资源A,想要去锁定一个资源B,而另一个线程中锁定了资源B,想...

    产生死锁的条件有四个

    互斥条件,

    进程在某一时间内独占资源

    请求与保持条件

    一个进程因请求资源堵塞时,对于获得的资源保持不放,

    不可剥夺条件

    已获得资源,在未使用完毕时,不能强行剥夺

    循环等待条件

    若干进程之间形成一种头尾相接的循环等待资源关系

    那么线程死锁是因为多线程,访问共享资源,由于访问的顺序不当所造成的的。通常是一个线程锁定了一个资源A,想要去锁定一个资源B,而另一个线程中锁定了资源B,想去锁定一个资源A已完成自身操作,而两个线程都不愿放弃自身资源,造成两个线程都在等待,而无法执行的情况。

    要解决死锁就要从四个条件出发,只要破坏一个必要条件,那么死锁就解决了。

    展开全文
  • 下面小编就为大家带来一篇多线程死锁的产生以及如何避免死锁方法(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 如何避免Java死锁? 是Java面试中最受欢迎的问题之一,也是本季多线程的风格,主要是在高层提出,并带有很多后续问题。 尽管问题看起来很基础,但是一旦您开始深入研究,大多数Java开发人员就会陷入困境。 面试...
  • 什么是死锁? 锁,顾名思义,含义真的就是我们平常每天看到的那个锁,锁门的锁,如果门锁着,那就进不去了,那就只能在门外等着。 软件中的锁,意义和这个类似,也是为了阻止非授权用户能够进入某些代码的执行,...
  • 怎么是死锁? 一个线程永远地持有一个锁,并且其他线程都尝试去获得这个锁时,那么它们将永远被阻塞。假如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得锁L,那么这两个线程将永远等待下去,这种情况就是最...
  • 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。...下面是一个多线程死锁的例子: public class lock{ ...
  • 本篇文章主要介绍了Java多线程死锁的出现和解决方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 什么是死锁?如何解决死锁?

    千次阅读 2020-08-11 18:45:45
    死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为...
  • 如何避免多线程死锁?

    千次阅读 2017-09-08 23:23:09
    如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得锁L,那么这两个线程将永远等待下去,这种情况就是最简单的死锁形式。 在数据库系统的设计中考虑了监测死锁以及从死锁中恢复,数据库如果监测到了一组事物...
  • 这篇文章主要介绍了Java线程死锁实例及解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下1、死锁的定义所谓死锁是指多个线程因竞争资源而造成的一种僵局...
  • 什么是死锁?如何避免死锁?

    万次阅读 多人点赞 2018-09-01 10:15:10
     线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行。当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,...
  • 实测有效的一个c++检测线程死锁解决方法(实现和测试代码) 原创实测有效的一个c++检测线程死锁解决方法,已应用于项目,实测有效 原创文章地址:https://blog.csdn.net/liaozhilong88/article/details/80354414...
  • 主要介绍了python 多线程死锁问题的解决方案,帮助大家更好的理解和学习python 锁,感兴趣的朋友可以了解下
  • 一、什么是死锁? 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在...
  • 突然发现我的图解系统缺了「死锁」的内容,这就来补下。 在面试过程中,死锁也是高频的考点,因为如果线上环境真多发生了死锁,那真的出大事了。 这次,我们就来系统地聊聊死锁的问题。 死锁的概念; 模拟死锁问题...
  • 死锁的本质,举个例子如果此时有一个线程 A ,按照先获持有锁 a 再获取锁 b的顺序获得锁,同时另外一个线程 B,按照先获取锁 b 再获取锁 a 的顺序获取锁。如下图所示: 接着我们用代码模拟上线的执行过程 ...
  • 死锁一定发生在并发环境中,死锁是一种状态,当两个(或者多个线程)相互持有对方所需要的资源,却又都不主动释放手中持有的资源,导致大家都获取不到自己想要的资源,所有相关的线程无法继续执行。 死锁案例 import ...
  • 什么是线程死锁 是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 线程死锁怎么发生 发生死锁的情况一般是两个对象的锁相互等待造成的。 死锁...
  • 死锁是什么?如何避免死锁?

    千次阅读 2020-03-21 16:36:15
    本文尽量以最简洁的示例来帮助你快速理解,掌握死锁发生的原因及其解决方法。在阅读接下来的内容之前,你必须具备java中独占锁与线程之间通信的基本知识。 【Will见解】独占锁?线程之间通信? 死锁 : 当线程A...
  • 什么是死锁?如何分析死锁和避免死锁? 死锁就是在多线程运行时,线程对象在获取操作资源时,操作资源被其他线程对象占用而获取不到,导致线程运行阻塞 首先用jsp命令查看当前进程pid是否发生死锁,然后利用可视化...
  • 线程死锁的产生以及如何避免死锁

    万次阅读 多人点赞 2016-07-13 11:07:45
    所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。 下面我们通过一些实例来说明死锁现象。 先看生活中的一个实例,在一条河上有一座桥,桥面很窄,只能容纳...
  • 一、死锁定义1、生活中的列子两人吃饭,但只有一双筷子,2人轮流吃(同时拥有2只筷子才能吃),...这就是死锁2、定义指多线程因竞争资源而造成的一种僵局(互相等待)若无外力作用这些进程都将无法向前推进 。二、死锁...
  • Java线程死锁解决方案

    千次阅读 2016-09-24 00:13:57
    要了解线程死锁,首先要明白什么是死锁 死锁 通俗点讲:死锁就是两个或两个以上的进程或线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。 ...
  • ## 什么是死锁? 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 134,060
精华内容 53,624
关键字:

如何解决线程死锁?