• 什么是活锁和饥饿?


    活锁

    任务没有被阻塞,由于某些条件没有满足,导致一直重复尝试—失败—尝试—失败的过程。 处于活锁的实体是在不断的改变状态,活锁有可能自行解开。

    死锁是大家都拿不到资源都占用着对方的资源,而活锁是拿到资源却又相互释放不执行。

    解决活锁的一个简单办法就是在下一次尝试获取资源之前,随机休眠一小段时间。

    看一下,我们之前的一个例子,如果最后不进行随机休眠,就会产生活锁,现象就是很长一段时间,两个线程都在不断尝试获取和释放锁。

    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();
        }
        
    }

    饥饿

    一个线程因为 CPU 时间全部被其他线程抢占而得不到 CPU 运行时间,导致线程无法执行。

    产生饥饿的原因:

    • 优先级线程吞噬所有的低优先级线程的 CPU 时间
    • 其他线程总是能在它之前持续地对该同步块进行访问,线程被永久堵塞在一个等待进入同步块
    • 其他线程总是抢先被持续地获得唤醒,线程一直在等待被唤醒
    package constxiong.concurrency.a024;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.Future;
    
    /**
     * 测试线程饥饿
     * @author ConstXiong
     */
    public class TestThreadHungry {
    
        private static ExecutorService es = Executors.newSingleThreadExecutor();
        
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            Future<String> future1 = es.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    System.out.println("提交任务1");
                    Future<String> future2 = es.submit(new Callable<String>() {
                        @Override
                        public String call() throws Exception {
                            System.out.println("提交任务2");
                            return "任务 2 结果";
                        }
                    });
                    return future2.get();
                }
            });
            System.out.println("获取到" + future1.get());
        }
        
    }

    打印结果如下,线程池卡死。线程池只能容纳 1 个任务,任务 1 提交任务 2,任务 2 永远得不到执行。

    提交任务1

      

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

  • 相关阅读:
    AODV路由协议的路由缓存队列详解
    NS各种常用资料(转)
    Zigbee之旅(二):第一个CC2430程序——LED灯闪烁实验(转)
    计算机核心期刊一览【转】
    NS2中能量模型的添加
    Zigbee之旅(一):开天辟地(转)
    NS2能量模型
    Zigbee之旅(三):几个重要的CC2430基础实验——外部中断(转)
    如何画MDI主窗体的背景
    Speed up the display of Delphi list components
  • 原文地址:https://www.cnblogs.com/ConstXiong/p/11688000.html
Copyright © 2020-2023  润新知