【Java|多线程与高并发】死锁以及哲学家吃饭问题

简介: 死锁(Deadlock)是多线程编程中的一个常见问题,指的是两个或多个线程相互等待对方释放资源,导致程序无法继续执行的状态。

1. 什么是死锁

死锁(Deadlock)是多线程编程中的一个常见问题,指的是两个或多个线程相互等待对方释放资源,导致程序无法继续执行的状态。


在一种典型的死锁情况中,有两个或多个线程,每个线程都在持有一个资源的同时试图获得另一个线程持有的资源。当两个线程都在等待对方释放资源时,它们将永远无法继续执行,产生了死锁。


代码示例:


public class Demo26 {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(() ->{
            System.out.println("t1获取locker1");
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker2) {
                    System.out.println("t1获取locker2");
                }
            }
        });
        Thread t2 = new Thread(() ->{
            System.out.println("t2获取locker2");
            synchronized (locker2) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                synchronized (locker1) {
                    System.out.println("t2获取locker1");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

运行结果:


d23371212284473eb9814d2cac18b7bd.png


解释: t1对locker1加锁,t2对locker2加锁. 然后t1要获取locker2,t2要获取locker1. 但是t1和t2并没有执行完代码,并不会释放锁,需要获取对方的锁之后,才会释放锁. 因此它们就"僵"住了. 代码执行不下去了.


之前看到过一句话也能很好的理解这个问题,比如你去面试,面试官问你一个问题. 你回答说"你先给我发offer,我就回答问题". 面试官说"你先回答问题,我再给你发offer".


2. 哲学家吃饭问题

死锁还有一个很经典的问题,就是哲学家吃饭问题.


哲学家吃饭问题: 有五位哲学家围坐在一张圆形餐桌周围,每个哲学家需要交替进行思考和进餐。在餐桌上有五只筷子,每个哲学家的左右两边分别放有一只筷子。哲学家只能同时使用自己左右两边的筷子来进餐,而当一个哲学家使用筷子时,其他哲学家必须等待。


如图所示:


dbf203a8c0164ceb87e214a83bc40ed4.png

上述可能出现死锁的原因是: 两个及以上的哲学家可能会去争夺同一根筷子.


极端情况就是五个哲学家同时拿起自己左手边的一根筷子,想去拿右边的筷子,却发现已经被拿了. 谁也吃不到饭,无法释放筷子(锁).


解决上述问题的关键就是 如何解决死锁问题.


3.如何解决死锁

死锁的四个必要条件:


1.互斥使用: 资源不能同时被多个线程持有,只能被一个线程独占。

2.不可抢占: 一个线程获得资源后,不能被其他线程强制性地抢占。

3.请求和保持: 一个线程持有一个资源的同时,又请求另一个线程持有的资源。

4.循环等待: 若干个线程之间形成一种循环等待资源的关系。

避免死锁,只需要让其中任意一个条件不满足即可.


其中1和2是锁的基本特性,无法改变. 因此只能从条件3和条件4方面下手.


而条件3虽然可能能行,但是可能会带来新的问题.因此需要从条件4上打破


可以给上述的筷子设置编号. 设置好加锁的循序,每次先给编号小的进行加锁就可以解决上述问题.


以哲学家吃饭的极端情况为例:


默认情况下,一个人一根筷子.


d801ca2bf9974589a1ecf911ec7e6bea.png



对加锁的顺序进行设置(先拿序号小的).


8c0b105ed32c4afb979c58a9502aa18c.png

这里的5号哲学家不会拿筷子.因为对于5号哲学家来说,5比1大,5号哲学家要拿1号筷子. 但1号筷子已经被拿了,所以5号哲学家就进入"阻塞等待"了. 那么4号玩家就可以继续拿起5号筷子进行吃饭,然后进行释放筷子(锁),接着是3号,2号,1号.等1号哲学家释放完1号筷子,那么此时5号哲学家就可以拿起1号筷子和5号筷子进行吃饭了


感谢你的观看!希望这篇文章能帮到你!

Java专栏在不断更新中,欢迎订阅!

“愿与君共勉,携手共进!”


相关文章
|
5天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
18 2
|
9天前
|
存储 缓存 Java
java线程内存模型底层实现原理
java线程内存模型底层实现原理
java线程内存模型底层实现原理
|
6天前
|
Java 调度
Java-Thread多线程的使用
这篇文章介绍了Java中Thread类多线程的创建、使用、生命周期、状态以及线程同步和死锁的概念和处理方法。
Java-Thread多线程的使用
|
9天前
|
Java 调度 开发者
Java中的多线程编程:从基础到实践
本文旨在深入探讨Java多线程编程的核心概念和实际应用,通过浅显易懂的语言解释多线程的基本原理,并结合实例展示如何在Java中创建、控制和管理线程。我们将从简单的线程创建开始,逐步深入到线程同步、通信以及死锁问题的解决方案,最终通过具体的代码示例来加深理解。无论您是Java初学者还是希望提升多线程编程技能的开发者,本文都将为您提供有价值的见解和实用的技巧。
15 2
|
4天前
|
Java 数据中心 微服务
Java高级知识:线程池隔离与信号量隔离的实战应用
在Java并发编程中,线程池隔离与信号量隔离是两种常用的资源隔离技术,它们在提高系统稳定性、防止系统过载方面发挥着重要作用。
5 0
|
6天前
|
Java 数据处理 调度
Java中的多线程编程:从基础到实践
本文深入探讨了Java中多线程编程的基本概念、实现方式及其在实际项目中的应用。首先,我们将了解什么是线程以及为何需要多线程编程。接着,文章将详细介绍如何在Java中创建和管理线程,包括继承Thread类、实现Runnable接口以及使用Executor框架等方法。此外,我们还将讨论线程同步和通信的问题,如互斥锁、信号量、条件变量等。最后,通过具体的示例展示了如何在实际项目中有效地利用多线程提高程序的性能和响应能力。
|
7天前
|
安全 算法 Java
Java中的多线程编程:从基础到高级应用
本文深入探讨了Java中的多线程编程,从最基础的概念入手,逐步引导读者了解并掌握多线程开发的核心技术。无论是初学者还是有一定经验的开发者,都能从中获益。通过实例和代码示例,本文详细讲解了线程的创建与管理、同步与锁机制、线程间通信以及高级并发工具等主题。此外,还讨论了多线程编程中常见的问题及其解决方案,帮助读者编写出高效、安全的多线程应用程序。
|
9天前
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
5月前
|
存储 安全 Java
深入理解Java并发编程:线程安全与锁机制
【5月更文挑战第31天】在Java并发编程中,线程安全和锁机制是两个核心概念。本文将深入探讨这两个概念,包括它们的定义、实现方式以及在实际开发中的应用。通过对线程安全和锁机制的深入理解,可以帮助我们更好地解决并发编程中的问题,提高程序的性能和稳定性。
|
2月前
|
存储 安全 Java
解锁Java并发编程奥秘:深入剖析Synchronized关键字的同步机制与实现原理,让多线程安全如磐石般稳固!
【8月更文挑战第4天】Java并发编程中,Synchronized关键字是确保多线程环境下数据一致性与线程安全的基础机制。它可通过修饰实例方法、静态方法或代码块来控制对共享资源的独占访问。Synchronized基于Java对象头中的监视器锁实现,通过MonitorEnter/MonitorExit指令管理锁的获取与释放。示例展示了如何使用Synchronized修饰方法以实现线程间的同步,避免数据竞争。掌握其原理对编写高效安全的多线程程序极为关键。
52 1

热门文章

最新文章

下一篇
无影云桌面