什么是活锁和饥饿

参考答案

1.  活锁

  • 任务没有被阻塞,由于某些条件没有满足,导致一直重复尝试—失败—尝试—失败的过程。 处于活锁的实体是在不断的改变状态,活锁有可能自行解开。
  • 死锁是大家都拿不到资源都占用着对方的资源,而活锁是拿到资源却又相互释放不执行。
  • 解决活锁的一个简单办法就是在下一次尝试获取资源之前,随机休眠一小段时间。如果最后不进行随机休眠,就会产生活锁,现象就是很长一段时间,两个线程都在不断尝试获取和释放锁。

实例:

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

2.  饥饿

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

产生饥饿的原因:

  • 优先级线程吞噬所有的低优先级线程的 CPU 时间
  • 其他线程总是能在它之前持续地对该同步块进行访问,线程被永久堵塞在一个等待进入同步块
  • 其他线程总是抢先被持续地获得唤醒,线程一直在等待被唤醒
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

以上,是Java面试题【什么是活锁和饥饿】的参考答案。

 

输出,是最好的学习方法

欢迎在评论区留下你的问题、笔记或知识点补充~

—end—

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧