精华内容
下载资源
问答
  • 线程死锁

    万次阅读 多人点赞 2019-09-03 23:12:56
    线程死锁是指两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这...

    死锁(DeadLock)

    死锁概述

    线程死锁是指两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁。

    死锁产生的条件
    • 互斥条件:一个资源,或者说一个锁只能被一个线程所占用,当一个线程首先获取到这个锁之后,在该线程释放这个锁之前,其它线程均是无法获取到这个锁的。
    • 占有且等待:一个线程已经获取到一个锁,再获取另一个锁的过程中,即使获取不到也不会释放已经获得的锁。
    • 不可剥夺条件:任何一个线程都无法强制获取别的线程已经占有的锁
    • 循环等待条件:线程A拿着线程B的锁,线程B拿着线程A的锁。。
    死锁演示
    package com.github.excellent01;
    
    /**
     * @auther plg
     * @date 2019/5/18 12:12
     */
    public class DeadLock {
        private  Object lock1 = new Object();
        private  Object lock2 = new Object();
    
        public  void method1() throws InterruptedException {
            synchronized(lock1){
                System.out.println(Thread.currentThread().getName() + "获取到lock1,请求获取lock2....");
                Thread.sleep(1000);
                synchronized (lock2){
                    System.out.println("获取到lock2....");
                }
            }
        }
        public  void method2() throws InterruptedException {
            synchronized(lock2){
                System.out.println(Thread.currentThread().getName() + "获取到lock2,请求获取lock1....");
                Thread.sleep(1000);
                synchronized (lock1){
                    System.out.println("获取到lock1....");
                }
            }
        }
    
        public static void main(String[] args) {
            DeadLock deadLock = new DeadLock();
    
            new Thread(()-> {
                try {
                    deadLock.method1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
            new Thread(()-> {
                try {
                    deadLock.method2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
    
        }
    }
    
    
    

    运行结果:
    在这里插入图片描述
    这样便造成了死锁,Thread1拿着lock1,想要lock2,Thread2拿着lock2,想要lock1.

    如何避免死锁
    • 加锁顺序:线程按照相同的顺序加锁。
    • 加锁时限,线程获取锁的过程中限制一定的时间,如果给定时间内获取不到,就算了,别勉强自己。这需要用到Lock的一些API。
    死锁检测

    在发生死锁之后,程序就卡住了没有任何反应,但程序仍在运行,因此需要借助一些

    • 首先使用 jps -l 显示正在运行的虚拟机进程,并显示虚拟机执行主类(main函数所在
      的类)名称以及这些进程的本地虚拟机唯一ID

    在这里插入图片描述

    • 使用 jstack + id 进行跟踪排查

    在这里插入图片描述
    在这里插入图片描述
    死锁的原因,具体哪个线程,具体的行号都说明的很清楚。

    展开全文
  • 什么是线程死锁,? 产生死锁的四个必要条件? 解决线程死锁的办法是什么? 如何避免死锁? 什么是线程死锁? 首先是一个线程需要多把锁,并发的时候多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被...

    面试题

    1. 什么是线程死锁,?
    2. 产生死锁的四个必要条件?
    3. 解决线程死锁的办法是什么?
    4. 如何避免死锁?

    什么是线程死锁?

    首先是一个线程需要多把锁,并发的时候多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

    如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

    下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》):

    public class DeadLockDemo {
        private static Object resource1 = new Object();//资源 1
        private static Object resource2 = new Object();//资源 2
    
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource2");
                    synchronized (resource2) {
                        System.out.println(Thread.currentThread() + "get resource2");
                    }
                }
            }, "线程 1").start();
    
            new Thread(() -> {
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource1");
                    synchronized (resource1) {
                        System.out.println(Thread.currentThread() + "get resource1");
                    }
                }
            }, "线程 2").start();
        }
    }
    

    Output

    Thread[线程 1,5,main]get resource1
    Thread[线程 2,5,main]get resource2
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 2,5,main]waiting get resource1
    
    1. 线程 A 通过 synchronized (resource1)获得resource1 的监视器锁
    2. 通过Thread.sleep(1000);让线程A休眠1s为的是让线程B得到执行然后获取到resource2的监视器锁。
    3. 线程 A 和线程B休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。

    产生死锁的四个必要条件?

    • 互斥条件:该资源任意一个时刻只由一个线程占用。
    • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
    • 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
    • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

    如何避免死锁?

    • 破坏互斥条件: 这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。
    • 破坏请求与保持条件:
      一次性申请所有的资源。
    • 破坏不剥夺条件:
      占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
    • 破坏循环等待条件: 靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

    我们只要破坏产生死锁的四个条件中的其中一个就可以了,一般情况下解决线程死锁通常的解决办法是先对锁申请进行排序。

    我们对线程 2 的代码修改成下面这样就不会产生死锁了

    new Thread(() -> {
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource2");
                    synchronized (resource2) {
                        System.out.println(Thread.currentThread() + "get resource2");
                    }
                }
            }, "线程 2").start();
    

    Output

    Thread[线程 1,5,main]get resource1
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 1,5,main]get resource2
    Thread[线程 2,5,main]get resource1
    Thread[线程 2,5,main]waiting get resource2
    Thread[线程 2,5,main]get resource2
    
    Process finished with exit code 0
    

    我们分析一下上面的代码为什么避免了死锁的发生?

    线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。

    再举一个工作中死锁的例子(丁威老师的案例)

    import java.util.List;
    public class LockTest {
        
        // 例如这个方法上有一个事务注解
        public void updateBusiness(List<Long> goodsIds) {
    
            if(goodsIds == null || goodsIds.isEmpty()) {
                return;
            }
            IGoodsDao goodsDao = null;
    
            for(Long gid : goodsIds) {
                goodsDao.updateGoods(gid, 1);  // 将库存减去1,需要持有该记录的行锁
            }
        }
    }
    
    interface  IGoodsDao {
        // 减库存
        void updateGoods(Long goodsId, int nums);
    }
    
    1. 如果一个用户要购买商品ID 为 1,3 的商品
    2. 而另外一个用户需要购买同样的商品,但是在购物车中选择商品的顺序是 3,1
    3. 此时两个线程同时调用 updateBusiness 方法

    执行轨迹如下:

    这样就出现了死锁。

    避免死锁的方法:
    1. 数据库锁的加锁和解锁需要在同一个数据库连接里,否则会出现解锁失败
    2. 避免一个线程获取多个锁
    3. 尝试使用定时锁,lock.tryLock(timeOut)替换内部锁
    4. 避免一个锁内同时占用多个资源,尽量保证每一个锁内只占用一个资源

    针对上面的例子,通常的解决办法是,先对锁申请进行排序。

    // 例如这个方法上有一个事务注解
        public void updateBusiness(List<Long> goodsIds) {
    
            if(goodsIds == null || goodsIds.isEmpty()) {
                return;
            }
            IGoodsDao goodsDao = null;
    
            Collections.sort(goodsIds);
    
            for(Long gid : goodsIds) {
                goodsDao.updateGoods(gid, 1);  // 将库存减去1,需要持有该记录的行锁
            }
        }
    

    这样就不会出现死锁了。

    如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。 感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。

    原创不易,欢迎转发,关注公众号“码农进阶之路”,获取更多面试题,源码解读资料!
    在这里插入图片描述

    展开全文
  • 检测线程死锁,告诉你如何检测线程死锁,学习一下吧
  • 点击上方“码农进阶之路”,选择“设为星标”回复“面经”获取面试资料面试题什么是线程死锁,?产生死锁的四个必要条件?解决线程死锁的办法是什么?如何避免死锁?什么是线程死锁?首先是一个线程需...

    点击上方“码农进阶之路”,选择“设为星标”

    回复“面经”获取面试资料

    面试题

    1. 什么是线程死锁,?

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

    3. 解决线程死锁的办法是什么?

    4. 如何避免死锁?

    什么是线程死锁?

    首先是一个线程需要多把锁,并发的时候多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

    如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

    下面通过一个例子来说明线程死锁,代码模拟了上图的死锁的情况 (代码来源于《并发编程之美》):

    public class DeadLockDemo {
        private static Object resource1 = new Object();//资源 1
        private static Object resource2 = new Object();//资源 2
    
    
        public static void main(String[] args) {
            new Thread(() -> {
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource2");
                    synchronized (resource2) {
                        System.out.println(Thread.currentThread() + "get resource2");
                    }
                }
            }, "线程 1").start();
    
    
            new Thread(() -> {
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource1");
                    synchronized (resource1) {
                        System.out.println(Thread.currentThread() + "get resource1");
                    }
                }
            }, "线程 2").start();
        }
    }
    

    Output

    Thread[线程 1,5,main]get resource1
    Thread[线程 2,5,main]get resource2
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 2,5,main]waiting get resource1
    
    1. 线程 A 通过 synchronized (resource1)获得resource1 的监视器锁

    2. 通过Thread.sleep(1000);让线程A休眠1s为的是让线程B得到执行然后获取到resource2的监视器锁。

    3. 线程 A 和线程B休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。

    产生死锁的四个必要条件?

    • 互斥条件:该资源任意一个时刻只由一个线程占用。

    • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

    • 不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。

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

    如何避免死锁?

    • 破坏互斥条件: 这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。

    • 破坏请求与保持条件: 一次性申请所有的资源。

    • 破坏不剥夺条件: 占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

    • 破坏循环等待条件: 靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

    我们只要破坏产生死锁的四个条件中的其中一个就可以了,一般情况下解决线程死锁通常的解决办法是先对锁申请进行排序。

    我们对线程 2 的代码修改成下面这样就不会产生死锁了

    new Thread(() -> {
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get resource2");
                    synchronized (resource2) {
                        System.out.println(Thread.currentThread() + "get resource2");
                    }
                }
            }, "线程 2").start();
    

    Output

    Thread[线程 1,5,main]get resource1
    Thread[线程 1,5,main]waiting get resource2
    Thread[线程 1,5,main]get resource2
    Thread[线程 2,5,main]get resource1
    Thread[线程 2,5,main]waiting get resource2
    Thread[线程 2,5,main]get resource2
    
    
    Process finished with exit code 0
    

    我们分析一下上面的代码为什么避免了死锁的发生?

    线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。

    再举一个工作中死锁的例子(丁威老师的案例)

    import java.util.List;
    public class LockTest {
        
        // 例如这个方法上有一个事务注解
        public void updateBusiness(List<Long> goodsIds) {
    
    
            if(goodsIds == null || goodsIds.isEmpty()) {
                return;
            }
            IGoodsDao goodsDao = null;
    
    
            for(Long gid : goodsIds) {
                goodsDao.updateGoods(gid, 1);  // 将库存减去1,需要持有该记录的行锁
            }
        }
    }
    
    
    interface  IGoodsDao {
        // 减库存
        void updateGoods(Long goodsId, int nums);
    }
    
    1. 如果一个用户要购买商品ID 为  1,3 的商品

    2. 而另外一个用户需要购买同样的商品,但是在购物车中选择商品的顺序是 3,1

    3. 此时两个线程同时调用 updateBusiness 方法

    执行轨迹如下:

    这样就出现了死锁。

    避免死锁的方法:
    1. 数据库锁的加锁和解锁需要在同一个数据库连接里,否则会出现解锁失败

    2. 避免一个线程获取多个锁

    3. 尝试使用定时锁,lock.tryLock(timeOut)替换内部锁

    4. 避免一个锁内同时占用多个资源,尽量保证每一个锁内只占用一个资源

    针对上面的例子,通常的解决办法是,先对锁申请进行排序。

    // 例如这个方法上有一个事务注解
        public void updateBusiness(List<Long> goodsIds) {
    
    
            if(goodsIds == null || goodsIds.isEmpty()) {
                return;
            }
            IGoodsDao goodsDao = null;
    
    
            Collections.sort(goodsIds);
    
    
            for(Long gid : goodsIds) {
                goodsDao.updateGoods(gid, 1);  // 将库存减去1,需要持有该记录的行锁
            }
        }
    

    这样就不会出现死锁了。

    如果你觉得文章还不错,你的转发、分享、赞赏、点赞、留言就是对我最大的鼓励。感谢您的阅读,我坚持原创,十分欢迎并感谢您的关注。 

     原创不易,欢迎转发,求个关注,文末右下角赏个"在看"吧。

    往期精选

    上‍一篇‍:原创|《菜鸟读并发》多线程问题必知必会概念

    下一篇:原创|《菜鸟读并发》多线程程序如何调试?

    码农进阶之路

    长按二维码关注 

    面经 | 原理 | 源码 | 实战 | 工具

    点击留言

    展开全文
  • java线程死锁代码示例

    2020-08-28 18:02:59
    主要介绍了java线程死锁代码示例,分享了一个简单线程死锁的例子,需要的朋友可以参考下。
  • 一、导致线程死锁的原因 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。这是从...

    欢迎大家关注我的公众号【老周聊架构】,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多线程死锁示例

    2020-08-27 00:23:39
    主要介绍了Java多线程死锁,结合实例形式分析了Java多线程出现死锁的相关原因与操作注意事项,需要的朋友可以参考下
  • IOS 线程死锁详细介绍

    2020-08-31 06:27:39
    主要介绍了IOS 线程死锁详细介绍的相关资料,需要的朋友可以参考下
  • 绍Java Swing多线程死锁问题解析
  • lookcop 线程死锁检测工具
  • 7.13 线程死锁

    2020-03-19 22:13:05
    线程死锁简介 简介 当两个线程相互等待对方释放“锁”时就会发生死锁 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续 过多的同步可能会造成死锁。 ...
  • c++多线程死锁检测

    2019-01-03 18:20:20
    一个简单有效的即时检测线程死锁的方法(附c++源代码) 原文链接:https://blog.csdn.net/liaozhilong88/article/details/80354414 原链接是windows下实现,这份代码以pthread简单实现跨平台 感谢原博主分享
  • 线程死锁问题

    2017-03-17 22:23:59
    线程死锁就是线程之前相互争夺对面拥有的资源,并且不释放资源,这样下去就会造成死锁。(个人见解) package cn.sp.test02; /** * * @author 2YSP *线程死锁问题 */ public class TestDeadLock { //是指不同...
  • 什么是线程死锁 是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 线程死锁怎么发生 发生死锁的情况一般是两个对象的锁相互等待造成的。 死锁...
  • 主要介绍了 Java 线程死锁的问题解决办法的相关资料,希望通过本大家能帮助到大家,遇到类似问题能够解决,需要的朋友可以参考下
  • 主要介绍了Java线程死锁实例及解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • java模拟线程死锁

    2014-01-28 15:19:43
    java线程实践,了解线程死锁,同步问题
  • 主要介绍了java 多线程死锁详解及简单实例的相关资料,需要的朋友可以参考下

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 204,963
精华内容 81,985
关键字:

线程死锁