面试17解析-死锁

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 请结合经典案例-哲学家就餐,来谈谈你对死锁的理解,以及怎么预防和解除死锁?

本文阅读大概需要20分钟。


这个题目主要考查死锁相关的问题。


一哲学家就餐


描述:在一张圆桌上,有n个哲学家,n支筷子,他们的生活方式只是交替地进行思考和进餐,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,进餐完毕,放下筷子又继续思考。


根据描述,实现代码如下:


public class Question17 {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        int sum = 5;
        Chopstick[] chopsticks = new Chopstick[sum];
        for (int i = 0; i < sum; i++) {
            chopsticks[i] = new Chopstick(i);
        }
        for (int i = 0; i < sum; i++) {
            exec.execute(new Philosopher(chopsticks[i], chopsticks[(i + 1) % sum], i));
        }
    }
}
// 筷子
class Chopstick {
    //筷子位置
    private int id;
    //状态
    private boolean isUsed = false;
    public Chopstick(int id) {
        this.id = id;
    }
    //拿取
    public synchronized void take() throws InterruptedException {
        while (isUsed) {
            wait();
        }
        System.err.println(this + " 被使用");
        isUsed = true;
    }
    //放下
    public synchronized void drop() {
        isUsed = false;
        System.err.println(this + " 被放下");
        notifyAll();
    }
    @Override
    public String toString() {
        return "筷子[" + id + ']';
    }
}
// 哲学家
class Philosopher implements Runnable {
    private Chopstick left;
    private Chopstick right;
    private int id;
    private Random rand = new Random();
    public Philosopher(Chopstick left, Chopstick right, int id) {
        this.left = left;
        this.right = right;
        this.id = id;
    }
    @Override
    public void run() {
        while (!Thread.interrupted()) {
            try {
                think();
                System.out.println(this + " 想吃饭!");
                eat();
            } catch (InterruptedException e) {
                System.err.println(this+" InterruptedException");
            }
        }
    }
    //思考
    private void think() throws InterruptedException {
        System.out.println(this + " 思考...");
        TimeUnit.MILLISECONDS.sleep(rand.nextInt(1) * 100);
    }
    //吃饭
    private void eat() throws InterruptedException {
        left.take();
        right.take();
        System.out.println(this + " 正在吃饭...");
        TimeUnit.MILLISECONDS.sleep(rand.nextInt(2) * 100);
        left.drop();
        right.drop();
    }
    @Override
    public String toString() {
        return "哲学家[" + id + ']';
    }
}


运行结果如图所示:


image.png


通过运行结果,我们可以发现,到最后,没有一个哲学家能过同时获取两只筷子吃饭。


二为什么会产生死锁


死锁问题被认为是线程/进程间切换消耗系统性能的一种极端情况。在死锁时,线程/进程间相互等待资源,而又不释放自身的资源,导致无穷无尽的等待,其结果是任务永远无法执行完成。哲学家问题便是线程资源竞争导致的死锁现象,在程序运行一段时间后,程序所处的状态是n位哲学(n个线程)都各自获取了一只筷子的状态,此时所有哲学家都想获取第二只筷子去吃饭,但是共享资源n只筷子已经都被n位哲学家握在手里了,彼此想要的筷子都在其他哲学家手中,又没有机制能让任何哲学家放弃握在手中的筷子,从而照成了所有哲学家(所有线程)都在等待其他人手中资源的死锁问题。


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


  1. 互斥条件:一个资源每次只能被一个线程/进程使用。
  2. 请求与保持条件:一个线程/进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:线程/进程已获得的资源,在未使用完之前,不能强行剥夺。
  4. 循环等待条件:若干线程/进程之间形成一种头尾相接的循环等待资源关系。


三死锁的解除与预防


一般解决死锁的途径分为死锁的预防,避免,检测与恢复这三种。


死锁的预防是要求线程/进程申请资源时遵循某种协议,从而打破产生死锁的四个必要条件中的一个或几个,保证系统不会进入死锁状态。


死锁的避免不限制线程/进程有关申请资源的命令,而是对线程/进程所发出的每一个申请资源命令加以动态地检查,并根据检查结果决定是否进行资源分配。


死锁检测与恢复是指系统设有专门的机构,当死锁发生时,该机构能够检测到死锁发生的位置和原因,并能通过外力破坏死锁发生的必要条件,从而使得并发进程从死锁状态中恢复出来。


对于java程序来说,产生死锁时,我们可以用jvisualvm/jstack来分析程序产生的死锁原因,根据病因来根治。


相关文章
|
6天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
20 2
|
6天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
19 2
|
17天前
|
存储 NoSQL MongoDB
MongoDB面试专题33道解析
大家好,我是 V 哥。今天为大家整理了 MongoDB 面试题,涵盖 NoSQL 数据库基础、MongoDB 的核心概念、集群与分片、备份恢复、性能优化等内容。这些题目和解答不仅适合面试准备,也是日常工作中深入理解 MongoDB 的宝贵资料。希望对大家有所帮助!
|
14天前
|
SQL 算法 关系型数据库
面试:什么是死锁,如何避免或解决死锁;MySQL中的死锁现象,MySQL死锁如何解决
面试:什么是死锁,死锁产生的四个必要条件,如何避免或解决死锁;数据库锁,锁分类,控制事务;MySQL中的死锁现象,MySQL死锁如何解决
|
22天前
|
缓存 前端开发 JavaScript
"面试通关秘籍:深度解析浏览器面试必考问题,从重绘回流到事件委托,让你一举拿下前端 Offer!"
【10月更文挑战第23天】在前端开发面试中,浏览器相关知识是必考内容。本文总结了四个常见问题:浏览器渲染机制、重绘与回流、性能优化及事件委托。通过具体示例和对比分析,帮助求职者更好地理解和准备面试。掌握这些知识点,有助于提升面试表现和实际工作能力。
58 1
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
412 37
|
2月前
|
缓存 Android开发 开发者
Android RecycleView 深度解析与面试题梳理
本文详细介绍了Android开发中高效且功能强大的`RecyclerView`,包括其架构概览、工作流程及滑动优化机制,并解析了常见的面试题。通过理解`RecyclerView`的核心组件及其优化技巧,帮助开发者提升应用性能并应对技术面试。
89 8
|
2月前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
67 8
|
1月前
|
缓存 Java 程序员
Map - LinkedHashSet&Map源码解析
Map - LinkedHashSet&Map源码解析
67 0
|
1月前
|
算法 Java 容器
Map - HashSet & HashMap 源码解析
Map - HashSet & HashMap 源码解析
54 0

推荐镜像

更多