4 线程死锁
1 死锁定义
所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。死锁是多线程程序设计带来的负面效果,在程序设计时需要防止死锁的发生。
2 死锁的必要条件
(1) 互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
(2) 不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
(3) 请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
(4) 循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, ..., pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的资源被P0占有,如图1所示。
3 死锁实例
(1)同步代码块死锁

public class DeadLockTest { public static void main(String[] args) { Customer customer = new Customer(); Hotel hotel = new Hotel(); new Thread(new Dining(customer, hotel)).start(); new Thread(new Pay(customer, hotel)).start(); } } class Dining implements Runnable { private Customer customer; private Hotel hotel; public Dining(Customer customer, Hotel hotel) { this.customer = customer; this.hotel = hotel; } @Override public void run() { synchronized (customer) { customer.dining(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (hotel) { hotel.service(); } } } } class Pay implements Runnable { private Customer customer; private Hotel hotel; public Pay(Customer customer, Hotel hotel) { this.customer = customer; this.hotel = hotel; } @Override public void run() { synchronized (hotel) { hotel.bill(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (customer) { customer.pay(); } } } }

class Customer { public void pay() { System.out.println("顾客买单"); } public void dining() { System.out.println("顾客用餐"); } } class Hotel { public void service() { System.out.println("上菜"); } public void bill() { System.out.println("请先买单"); } }
(2) 同步方法死锁

public class DeadLockDemo { public static void main(String[] args) { new DeadThread(); } } class Customers { public synchronized void dining(Waiter w) { System.out.println("先吃饭再买单"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } w.doService(); } public synchronized void pay() { System.out.println("同意,先买单吧"); } } class Waiter { public synchronized void bill(Customers c) { System.out.println("先买单再吃饭"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } c.pay(); } public synchronized void doService() { System.out.println("同意,先吃饭吧"); } } class DeadThread implements Runnable { Customers customer = new Customers(); Waiter waiter = new Waiter(); public DeadThread() { new Thread(this).start(); waiter.bill(customer); } @Override public void run() { customer.dining(waiter); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?