zoukankan      html  css  js  c++  java
  • java死锁

    一、死锁产生的四个必要条件

    1.1、互斥

    即当资源被一个线程使用(占有)时,别的线程不能使用

    1.2、不可剥夺

    资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。

    1.3、请求和保持

    即当资源请求者在请求其他的资源的同时保持对原有资源的占有。

    1.4、循环等待

    即存在一个等待闭环链路:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。

    二、模拟死锁产生

    假如有这样的一个需求,线程需要获取MN两个数据资源之后,才能进行下一步的操作,MN获取顺序没有先后之分

    2.1、思路

    定义两个数据资源M、N,两个线程Thread-A、Thread-B分别获取资源M资源N两个资源

    2.2、synchronized实现

    package com.duchong.concurrent.thread;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 线程A和线程B同时去获取数据dataM和dataN
     * 每个线程获取两个锁
     * @author DUCHONG
     * @since 2020-09-04 15:02
     **/
    public class DeadLockDemo {
    
        //定义两个资源dataA和dataB
        public static final String DATA_M="dataM";
        public static final String DATA_N="dataN";
    
    
        public static void main(String[] args) {
    
            new Thread(new DeadThreadA(),"Thread-A").start();
    
            new Thread(new DeadThreadB(),"Thread-B").start();
    
        }
    
    
        static class DeadThreadA implements Runnable{
    
            public DeadThreadA() {
            }
    
            @Override
            public void run() {
                try {
    
                        System.out.println(Thread.currentThread().getName()+"---启动");
                        synchronized (DATA_M){
    
                            System.out.println(Thread.currentThread().getName()+"---获得数据M");
                            System.out.println(Thread.currentThread().getName()+"---尝试获取数据N");
                            //睡一会,模拟 线程B锁住数据B
                            TimeUnit.SECONDS.sleep(3L);
                            synchronized (DATA_N){
                                System.out.println(Thread.currentThread().getName()+"---获取到数据N");
                                System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
                            }
                        }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        static class DeadThreadB implements Runnable{
    
            public DeadThreadB() {
            }
    
            @Override
            public void run() {
                try {
    
                        System.out.println(Thread.currentThread().getName()+"---启动");
                        synchronized (DATA_N){
    
                            System.out.println(Thread.currentThread().getName()+"---获得数据N");
                            System.out.println(Thread.currentThread().getName()+"---尝试获取数据M");
    
                            synchronized (DATA_M){
                                System.out.println(Thread.currentThread().getName()+"---获取到数据M");
                                System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
                            }
                        }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    }
    
    

    2.3、ReentrantLock实现

    package com.duchong.concurrent.thread;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 线程A和线程B同时去获取数据dataM和dataN
     * 每个线程获取两个锁
     * @author DUCHONG
     * @since 2020-09-04 15:02
     **/
    public class DeadLockDemo3 {
    
        static Lock lockM=new ReentrantLock();
        static Lock lockN=new ReentrantLock();
    
        public static void main(String[] args) {
    
            new Thread(new DeadThreadA(),"Thread-A").start();
    
            new Thread(new DeadThreadB(),"Thread-B").start();
    
        }
    
    
        static class DeadThreadA implements Runnable{
    
            public DeadThreadA() {
            }
    
            @Override
            public void run() {
                try {
    
                        System.out.println(Thread.currentThread().getName()+"---启动");
                        lockM.lock();
                            System.out.println(Thread.currentThread().getName()+"---获得数据M");
                            System.out.println(Thread.currentThread().getName()+"---尝试获取数据N");
    
                        //睡一会,模拟 线程B锁住数据B
                        TimeUnit.SECONDS.sleep(3L);
    
                        lockN.lock();
                            System.out.println(Thread.currentThread().getName()+"---获取到数据N");
                            System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        static class DeadThreadB implements Runnable{
    
            public DeadThreadB() {
            }
    
            @Override
            public void run() {
                try {
    
                    System.out.println(Thread.currentThread().getName()+"---启动");
                    lockN.lock();
                        System.out.println(Thread.currentThread().getName()+"---获得数据N");
                        System.out.println(Thread.currentThread().getName()+"---尝试获取数据M");
    
                    lockM.lock();
                        System.out.println(Thread.currentThread().getName()+"---获取到数据M");
                        System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    }
    

    2.4、运行结果

    block

    2.5、VisualVM查看

    vm

    dump

    三、死锁解决方法

    3.1、synchronized死锁解决方法

    synchronized实现的死锁是由于在一个线程里面获取了两个锁导致的,如果一个线程每次只能获取一个锁,那么就不会出现由于嵌套持有锁导致的死锁,要达到既能一个线程每次获取一个锁,有能获取到两个资源的效果,可以把资源数据抽取出来放在一个独立的Data类里,然后让线程A线程B去获取

    3.1.1、Data类

    package com.duchong.concurrent.thread;
    
    /**
     * 资源数据类-单独抽出来
     * @author DUCHONG
     * @since 2020-09-04 17:57:43
     */
    public class Data{
    
            public static final String DATA_M="dataM";
            public static final String DATA_N="dataN";
    
             /**
              * 假如有一个线程持有dataM的锁,另一个线程进来,阻塞,更不用说进来的线程获取dataN了
              * 先获取数据B
              */
            public  void getData(){
                try {
    
                    System.out.println(Thread.currentThread().getName()+"---启动");
                    synchronized (Data.DATA_M){
    
                        System.out.println(Thread.currentThread().getName()+"---获得数据M");
                        System.out.println(Thread.currentThread().getName()+"---尝试获取数据N");
    
                        synchronized (Data.DATA_N){
                            System.out.println(Thread.currentThread().getName()+"---获取到数据N");
                            System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
        }
    

    3.1.2、线程A和线程B

    package com.duchong.concurrent.thread;
    
    /**
     * 将资源数据单独抽离出来
     * 每隔线程获取一把锁
     * @author DUCHONG
     * @since 2020-09-04 15:02
     **/
    public class DeadLockDemo2 {
    
        public static void main(String[] args) {
    
            new Thread(new DeadThreadA(),"Thread-A").start();
    
            new Thread(new DeadThreadB(),"Thread-B").start();
    
        }
    
    
    
        static class DeadThreadA implements Runnable{
    
            public DeadThreadA() {
            }
    
            @Override
            public void run() {
                new Data().getData();
            }
        }
    
        static class DeadThreadB implements Runnable{
    
            public DeadThreadB() {
            }
    
            @Override
            public void run() {
                new Data().getData();
            }
        }
    
    
    }
    

    3.1.3、运行结果

    noblock

    没有发生死锁,符合预期

    3.2、ReentrantLock死锁解决方法

    利用tryLock()方法尝试获取锁,设置超时时间为5s,如果获取不成功,释放已占有的资源给另外一个线程

    package com.duchong.concurrent.thread;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 线程A和线程B同时去获取数据dataM和dataN
     * 每个线程获取两个锁
     * @author DUCHONG
     * @since 2020-09-04 15:02
     **/
    public class DeadLockDemo4 {
    
    
        static Lock lockM=new ReentrantLock();
        static Lock lockN=new ReentrantLock();
    
        public static void main(String[] args) {
    
            new Thread(new DeadThreadA(),"Thread-A").start();
    
            new Thread(new DeadThreadB(),"Thread-B").start();
    
        }
    
    
        static class DeadThreadA implements Runnable{
    
            public DeadThreadA() {
            }
    
            @Override
            public void run() {
                try {
    
                    System.out.println(Thread.currentThread().getName()+"---启动");
                    //lockM 属于thread-a 独占
                    lockM.lock();
                        System.out.println(Thread.currentThread().getName()+"---获得数据M");
                        System.out.println(Thread.currentThread().getName()+"---尝试获取数据N");
    
                    //睡一会,模拟 线程B锁住数据B
                    TimeUnit.SECONDS.sleep(3L);
    
                    //以下代码不执行,此时lockN 属于thread-b 独占,thread-b释放lockN之后,thread-a 获取到lockN,在执行下面的代码
                    lockN.lock();
                            System.out.println(Thread.currentThread().getName()+"---获取到数据N");
                            System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
                    //thread-a 获取到lockN之后,释放lockM
                    lockM.unlock();
    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        static class DeadThreadB implements Runnable{
    
            public DeadThreadB() {
            }
    
            @Override
            public void run() {
                try {
    
                    System.out.println(Thread.currentThread().getName()+"---启动");
                    //此时lockN 属于thread-b 独占
                    lockN.lock();
                        System.out.println(Thread.currentThread().getName()+"---获得数据N");
                        System.out.println(Thread.currentThread().getName()+"---尝试获取数据M");
    
                    //thread-b尝试获取thread-a的lockM 5s ,失败则释放thread-b独占的 lockN,以防死锁
                    if (lockM.tryLock(5L, TimeUnit.SECONDS)) {
    
                        System.out.println(Thread.currentThread().getName()+"---获取到数据M");
                        System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
                    }
                    else{
                        //释放thread-b独占的 lockN,此时thread-a 可以获得lockN
                        lockN.unlock();
                        //thread-a 释放lockM后执行
                        lockM.lock();
                        System.out.println(Thread.currentThread().getName()+"---获取到数据M");
                        System.out.println(Thread.currentThread().getName()+"---数据M和N获取完毕,可以进行下一步操作");
                    }
    
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    
    }
    

    3.2.1、运行结果

    image-20200905004312148

    没有发生死锁。

  • 相关阅读:
    ASP.NET 实现邮件发送和接受的功能(Sockets)
    使用Sql server进行分布式查询
    Sqlserver中的一些技巧
    使用sql server中的全文索引
    水晶报表的装载和修改文本
    创建作业的通用存储过程
    MS SQL数据库备份和恢复
    数据库运用XML操作
    安装程序自动安装数据库
    ASP.NET 实现邮件发送和接受的功能(Mail)
  • 原文地址:https://www.cnblogs.com/geekdc/p/13616848.html
Copyright © 2011-2022 走看看