zoukankan      html  css  js  c++  java
  • 如何避免死锁

    并发程序一旦死锁,往往我们只能重启应用。解决死锁问题最好的办法就是避免死锁。

    死锁发生的条件

    • 互斥,共享资源只能被一个线程占用
    • 占有且等待,线程 t1 已经取得共享资源 s1,尝试获取共享资源 s2 的时候,不释放共享资源 s1
    • 不可抢占,其他线程不能强行抢占线程 t1 占有的资源 s1
    • 循环等待,线程 t1 等待线程 t2 占有的资源,线程 t2 等待线程 t1 占有的资源

    避免死锁的方法

    对于以上 4 个条件,只要破坏其中一个条件,就可以避免死锁的发生。

    对于第一个条件 "互斥" 是不能破坏的,因为加锁就是为了保证互斥。

    其他三个条件,我们可以尝试

    • 一次性申请所有的资源,破坏 "占有且等待" 条件
    •  占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏 "不可抢占" 条件
    •  按序申请资源,破坏 "循环等待" 条件

    使用管理类一次性申请所有的资源,破坏 "占有且等待" 条件示例

    package constxiong.concurrency.a023;
    
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * 测试 一次性申请所有的资源,破坏 "占有且等待" 条件示例
     * @author ConstXiong
     * @date 2019-09-24 14:04:12
     */
    public class TestBreakLockAndWait {
    
        //单例的资源管理类
        private final static Manger manager = new Manger();
        
        //资源1
        private static Object res1 = new Object();
        
        //资源2
        private static Object res2 = new Object();
        
        public static void main(String[] args) {
            new Thread(() -> {
                boolean applySuccess = false;
                while (!applySuccess) {
                    //向管理类,申请res1和res2,申请失败,重试
                    applySuccess = manager.applyResources(res1, res2);
                    if (applySuccess) {
                        try {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源成功");
                            synchronized (res1) {
                                System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res1 资源的锁");
                                //休眠 1秒
                                try {
                                    Thread.sleep(1000);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                synchronized (res2) {
                                    System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res2 资源的锁");
                                }
                            }
                        } finally {
                            manager.returnResources(res1, res2);//归还资源
                        }
                    } else {
                        System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源失败");
                        //申请失败休眠 200 毫秒后重试
                        try {
                            Thread.sleep(200);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            
            new Thread(() -> {
                boolean applySuccess = false;
                while (!applySuccess) {
                    //向管理类,申请res1和res2,申请失败,重试
                    applySuccess = manager.applyResources(res1, res2);
                    if (applySuccess) {
                        try {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源成功");
                            synchronized (res2) {
                                System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res1 资源的锁");
                                //休眠 1秒
                                try {
                                    Thread.sleep(1000);
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                synchronized (res1) {
                                    System.out.println("线程:" + Thread.currentThread().getName() + " 获取到 res2 资源的锁");
                                }
                            }
                        } finally {
                            manager.returnResources(res1, res2);//归还资源
                        }
                    } else {
                        System.out.println("线程:" + Thread.currentThread().getName() + " 申请 res1、res2 资源失败");
                        //申请失败休眠 200 毫秒后重试
                        try {
                            Thread.sleep(200);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            
        }
        
    }
    
    /**
     * 资源申请、归还管理类
     * @author ConstXiong
     * @date 2019-09-24 14:10:57
     */
    class Manger {
        
        //资源存放集合
        private Set<Object> resources = new HashSet<Object>();
        
        /**
         * 申请资源
         * @param res1
         * @param res2
         * @return
         */
        synchronized boolean applyResources(Object res1, Object res2) {
            if (resources.contains(res1) || resources.contains(res1)) {
                return false;
            } else {
                resources.add(res1);
                resources.add(res2);
                return true;
            }
        }
        
        /**
         * 归还资源
         * @param res1
         * @param res2
         */
        synchronized void returnResources(Object res1, Object res2) {
            resources.remove(res1);
            resources.remove(res2);
        }
        
    }

    打印结果如下,线程-1 在线程-0 释放完资源后才能成功申请 res1 和 res2 的锁

    线程:Thread-0 申请 res1、res2 资源成功
    线程:Thread-0 获取到 res1 资源的锁
    线程:Thread-1 申请 res1、res2 资源失败
    线程:Thread-1 申请 res1、res2 资源失败
    线程:Thread-1 申请 res1、res2 资源失败
    线程:Thread-1 申请 res1、res2 资源失败
    线程:Thread-1 申请 res1、res2 资源失败
    线程:Thread-0 获取到 res2 资源的锁
    线程:Thread-1 申请 res1、res2 资源失败
    线程:Thread-1 申请 res1、res2 资源成功
    线程:Thread-1 获取到 res1 资源的锁
    线程:Thread-1 获取到 res2 资源的锁

    使用 Lock 的 tryLock() 方法,获取锁失败释放所有资源,破坏 "不可抢占" 条件示例

    package constxiong.concurrency.a023;
    
    import java.util.Random;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * 测试 占有部分资源的线程进一步申请其他资源时,如果申请不到,主动释放它占有的资源,破坏 "不可抢占" 条件
     * @author ConstXiong
     * @date 2019-09-24 14:50:51
     */
    public class TestBreakLockOccupation {
        
        private static Random r = new Random(); 
    
        private static Lock lock1 = new ReentrantLock();
        
        private static Lock lock2 = new ReentrantLock();
        
        public static void main(String[] args) {
            new Thread(() -> {
                //标识任务是否完成
                boolean taskComplete = false;
                while (!taskComplete) {
                    lock1.lock();
                    System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 成功");
                    try {
                        //随机休眠,帮助造成死锁环境
                        try {
                            Thread.sleep(r.nextInt(30));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        
                        //线程 0 尝试获取 lock2
                        if (lock2.tryLock()) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 成功");
                            try {
                                taskComplete = true;
                            } finally {
                                lock2.unlock();
                            }
                        } else {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 失败");
                        }
                    } finally {
                        lock1.unlock();
                    }
                    
                    //随机休眠,避免出现活锁
                    try {
                        Thread.sleep(r.nextInt(10));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
            
            new Thread(() -> {
                //标识任务是否完成
                boolean taskComplete = false;
                while (!taskComplete) {
                    lock2.lock();
                    System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock2 成功");
                    try {
                        //随机休眠,帮助造成死锁环境
                        try {
                            Thread.sleep(r.nextInt(30));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        
                        //线程2 尝试获取锁 lock1
                        if (lock1.tryLock()) {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 成功");
                            try {
                                taskComplete = true;
                            } finally {
                                lock1.unlock();
                            }
                        } else {
                            System.out.println("线程:" + Thread.currentThread().getName() + " 获取锁 lock1 失败");
                        }
                    } finally {
                        lock2.unlock();
                    }
                    
                    //随机休眠,避免出现活锁
                    try {
                        Thread.sleep(r.nextInt(10));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        
    }

    打印结果如下

    线程:Thread-0 获取锁 lock1 成功
    线程:Thread-1 获取锁 lock2 成功
    线程:Thread-1 获取锁 lock1 失败
    线程:Thread-1 获取锁 lock2 成功
    线程:Thread-0 获取锁 lock2 失败
    线程:Thread-1 获取锁 lock1 成功
    线程:Thread-0 获取锁 lock1 成功
    线程:Thread-0 获取锁 lock2 成功

    按照一定的顺序加锁,破坏 "循环等待" 条件示例

    package constxiong.concurrency.a023;
    
    /**
     * 测试 按序申请资源,破坏 "循环等待" 条件
     * @author ConstXiong
     * @date 2019-09-24 15:26:23
     */
    public class TestBreakLockCircleWait {
    
        private static Object res1 = new Object();
        
        private static Object res2 = new Object();
        
        
        public static void main(String[] args) {
            new Thread(() -> {
                Object first = res1;
                Object second = res2;
                //比较 res1 和 res2 的 hashCode,如果 res1 的 hashcode > res2,交换 first 和 second。保证 hashCode 小的对象先加锁
                if (res1.hashCode() > res2.hashCode()) {
                    first = res2;
                    second = res1;
                }
                synchronized (first) {
                    System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + first + " 锁成功");
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized(second) {
                        System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + second + " 锁成功");
                    }
                }
            }).start();
            
            new Thread(() -> {
                Object first = res1;
                Object second = res2;
                //比较 res1 和 res2 的 hashCode,如果 res1 的 hashcode > res2,交换 first 和 second。保证 hashCode 小的对象先加锁
                if (res1.hashCode() > res2.hashCode()) {
                    first = res2;
                    second = res1;
                }
                synchronized (first) {
                    System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + first + " 锁成功");
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    synchronized(second) {
                        System.out.println("线程:" + Thread.currentThread().getName() + "获取资源 " + second + " 锁成功");
                    }
                }
            }).start();
        }
        
    }

    打印结果如下

    线程:Thread-0获取资源 java.lang.Object@7447157c 锁成功
    线程:Thread-0获取资源 java.lang.Object@7a80f45c 锁成功
    线程:Thread-1获取资源 java.lang.Object@7447157c 锁成功
    线程:Thread-1获取资源 java.lang.Object@7a80f45c 锁成功

     来一道刷了进BAT的面试题?

  • 相关阅读:
    Tcl与Design Compiler (九)——综合后的形式验证
    Tcl与Design Compiler (八)——DC的逻辑综合与优化
    Tcl与Design Compiler (七)——环境、设计规则和面积约束
    Tcl与Design Compiler (六)——基本的时序路径约束
    Tcl与Design Compiler (五)——综合库(时序库)和DC的设计对象
    Tcl与Design Compiler (四)——DC启动环境的设置
    Tcl与Design Compiler (三)——DC综合的流程
    python for循环及常用函数
    python 循环 while
    python 条件判断 if
  • 原文地址:https://www.cnblogs.com/ConstXiong/p/11687993.html
Copyright © 2011-2022 走看看