news 2026/5/1 9:38:48

Java多线程三大困境:死锁、活锁与饥饿的区别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java多线程三大困境:死锁、活锁与饥饿的区别

文章目录

  • Java多线程三大困境:死锁、活锁与饥饿的区别?
    • 一、线程世界的“三大煞星”
      • 1. 死锁(Deadlock)
        • 死锁的形成条件
        • 死锁的经典示例
      • 2. 活锁(Livelock)
        • 活锁的形成
        • 活锁的经典示例
      • 3. 饥饿(Starvation)
        • 饥饿的原因
        • 饥饿的经典示例
    • 二、如何解决这三大困境?
      • 1. 解决死锁
        • 预防死锁的示例
      • 2. 解决活锁
        • 解决不活锁的示例
      • 3. 解决饥饿
        • 使用公平锁的示例
    • 总结
    • 希望这篇文章能帮助你更好地理解这些多线程问题,并写出更健壮的代码!
      • 📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!

Java多线程三大困境:死锁、活锁与饥饿的区别?

大家好,闫工又来啦!今天我们要聊的是Java多线程编程中三个让人头大的问题——死锁活锁饥饿。这三个词听起来都像是线程的“终极 boss”,但实际上它们各有各的特点和解决办法。作为一名Java开发者,我们不仅要认识它们,还要学会如何在代码中避免这些陷阱。


一、线程世界的“三大煞星”

1. 死锁(Deadlock)

死锁是多线程编程中最常见的问题之一。简单来说,死锁是指两个或多个线程互相等待对方释放资源,导致所有相关线程都无法继续执行。这种情况下,程序会陷入停滞状态,就像两辆车在十字路口对峙,谁都不愿意先让开。

死锁的形成条件

要理解死锁,我们需要知道它的四个必要条件:

  1. 互斥条件:每个资源只能被一个线程占用。
  2. 不可剥夺条件:线程不能强行释放资源,必须主动释放。
  3. 请求和保持条件:线程在等待其他资源时,不会释放已经占有的资源。
  4. 循环等待条件:存在一组线程,每个线程都在等待下一个线程释放的资源。
死锁的经典示例

下面是一个经典的死锁代码示例:

publicclassDeadlockExample{publicstaticvoidmain(String[]args){// 创建两个对象作为资源Objectlock1=newObject();Objectlock2=newObject();// 线程A:先获取lock1,再获取lock2ThreadthreadA=newThread(()->{synchronized(lock1){System.out.println("Thread A has lock1");try{Thread.sleep(100);}catch(InterruptedExceptione){}synchronized(lock2){System.out.println("Thread A has both locks");}}});// 线程B:先获取lock2,再获取lock1ThreadthreadB=newThread(()->{synchronized(lock2){System.out.println("Thread B has lock2");try{Thread.sleep(100);}catch(InterruptedExceptione){}synchronized(lock1){System.out.println("Thread B has both locks");}}});threadA.start();threadB.start();}}

在这个例子中,线程A和线程B可能会同时持有不同的锁并等待对方的资源,最终导致死锁。


2. 活锁(Livelock)

活锁是另一种多线程问题。活锁是指线程不断尝试获取资源,但由于某种原因总是失败,从而无限循环而无法继续执行。与死锁不同,活锁中的线程仍然在运行,但它们无法完成任何有意义的工作。

活锁的形成

活锁通常发生在以下情况:

  1. 线程A和线程B都试图获取资源。
  2. 两者同时检测到冲突,然后尝试回退(比如释放已占用的资源)并重新开始。
  3. 如果它们不断重复这个过程,就会陷入活锁。
活锁的经典示例

下面是一个简单的活锁示例:

publicclassLivelockExample{publicstaticvoidmain(String[]args){// 创建两个线程,尝试同时获取资源ThreadthreadA=newThread(()->{while(true){synchronized(LivelockExample.class){System.out.println("Thread A is trying to get the lock");if(Math.random()<0.5){System.out.println("Thread A releases the lock");continue;// 释放锁并重新尝试}else{return;}}}});ThreadthreadB=newThread(()->{while(true){synchronized(LivelockExample.class){System.out.println("Thread B is trying to get the lock");if(Math.random()<0.5){System.out.println("Thread B releases the lock");continue;// 释放锁并重新尝试}else{return;}}}});threadA.start();threadB.start();}}

在这个例子中,两个线程可能会不断释放和重新获取锁,导致活锁。


3. 饥饿(Starvation)

饥饿是多线程编程中的另一个常见问题。饥饿是指某些线程由于优先级或其他原因,长时间得不到 CPU 的调度或资源的访问机会,从而无法完成任务

饥饿的原因

饥饿通常发生在以下情况:

  1. 优先级倒置:高优先级线程占据了资源,导致低优先级线程无法获取资源。
  2. 资源分配策略不合理:某些线程总是被“插队”,无法公平地获得资源。
饥饿的经典示例

下面是一个饥饿的示例:

publicclassStarvationExample{publicstaticvoidmain(String[]args){// 创建两个线程,一个高优先级,一个低优先级ThreadthreadA=newThread(()->{while(true){synchronized(StarvationExample.class){System.out.println("Thread A is running");try{Thread.sleep(100);}catch(InterruptedExceptione){}}}},"HighPriority");// 降低线程A的优先级threadA.setPriority(Thread.MIN_PRIORITY);ThreadthreadB=newThread(()->{while(true){synchronized(StarvationExample.class){System.out.println("Thread B is running");try{Thread.sleep(100);}catch(InterruptedExceptione){}}}},"LowPriority");// 提高线程B的优先级threadB.setPriority(Thread.MAX_PRIORITY);threadA.start();threadB.start();}}

在这个例子中,由于线程B的优先级更高,它可能会一直占用资源,导致线程A长时间得不到执行。


二、如何解决这三大困境?

1. 解决死锁

要避免死锁,我们需要打破其中一个必要条件。以下是几种常见的方法:

  • 预防:通过设计,避免同时获取多个锁。
  • 检测和恢复:在代码中定期检查是否发生了死锁,并采取措施恢复。
预防死锁的示例
publicclassDeadlockPrevention{publicstaticvoidmain(String[]args){// 确保所有线程都按相同的顺序获取锁ThreadthreadA=newThread(()->{synchronized(DeadlockPrevention.class){System.out.println("Thread A has lock1");try{Thread.sleep(100);}catch(InterruptedExceptione){}synchronized("string"){System.out.println("Thread A has both locks");}}});ThreadthreadB=newThread(()->{synchronized(DeadlockPrevention.class){System.out.println("Thread B has lock1");try{Thread.sleep(100);}catch(InterruptedExceptione){}synchronized("string"){System.out.println("Thread B has both locks");}}});threadA.start();threadB.start();}}

2. 解决活锁

要解决活锁,我们需要让线程在尝试获取资源时有一定的延迟或随机等待时间,以避免无限循环。

解决不活锁的示例
publicclassLivelockSolution{publicstaticvoidmain(String[]args){ThreadthreadA=newThread(()->{while(true){synchronized(LivelockSolution.class){System.out.println("Thread A is trying to get the lock");if(Math.random()<0.5){try{Thread.sleep(100);}catch(InterruptedExceptione){}continue;}else{return;}}}});ThreadthreadB=newThread(()->{while(true){synchronized(LivelockSolution.class){System.out.println("Thread B is trying to get the lock");if(Math.random()<0.5){try{Thread.sleep(100);}catch(InterruptedExceptione){}continue;}else{return;}}}});threadA.start();threadB.start();}}

3. 解决饥饿

要解决饥饿,我们需要确保所有线程都能公平地获得资源。以下是几种常见的方法:

  • 使用公平锁:Java 提供了ReentrantLock的公平模式。
  • 调整优先级策略:避免优先级倒置。
使用公平锁的示例
importjava.util.concurrent.locks.ReentrantLock;publicclassStarvationSolution{privatestaticReentrantLocklock=newReentrantLock(true);// 公平锁publicstaticvoidmain(String[]args){ThreadthreadA=newThread(()->{while(true){try{if(lock.tryLock()){System.out.println("Thread A acquired the lock");try{Thread.sleep(100);}catch(InterruptedExceptione){}lock.unlock();}}catch(Exceptione){}}},"HighPriority");threadA.setPriority(Thread.MIN_PRIORITY);ThreadthreadB=newThread(()->{while(true){try{if(lock.tryLock()){System.out.println("Thread B acquired the lock");try{Thread.sleep(100);}catch(InterruptedExceptione){}lock.unlock();}}catch(Exceptione){}}},"LowPriority");threadB.setPriority(Thread.MAX_PRIORITY);threadA.start();threadB.start();}}

总结

死锁、活锁和饥饿是多线程编程中的三大困境,但通过合理的设计和代码优化,我们可以有效地避免这些问题。记住以下几点:

  1. 预防死锁:确保所有线程按相同的顺序获取锁。
  2. 解决活锁:引入随机延迟或使用公平锁。
  3. 避免饥饿:使用公平锁并调整优先级策略。

希望这篇文章能帮助你更好地理解这些多线程问题,并写出更健壮的代码!

📚 领取 | 1000+ 套高质量面试题大合集(无套路,闫工带你飞一把)!

成体系的面试题,无论你是大佬还是小白,都需要一套JAVA体系的面试题,我已经上岸了!你也想上岸吗?

闫工精心准备了程序准备面试?想系统提升技术实力?闫工精心整理了1000+ 套涵盖前端、后端、算法、数据库、操作系统、网络、设计模式等方向的面试真题 + 详细解析,并附赠高频考点总结、简历模板、面经合集等实用资料!

✅ 覆盖大厂高频题型
✅ 按知识点分类,查漏补缺超方便
✅ 持续更新,助你拿下心仪 Offer!

📥免费领取👉 点击这里获取资料

已帮助数千位开发者成功上岸,下一个就是你!✨

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 11:12:37

Llama-Factory是否支持医学术语标准化?医疗AI重点应用

Llama-Factory 是否支持医学术语标准化&#xff1f;——医疗 AI 中的关键实践路径 在智能医疗系统日益普及的今天&#xff0c;一个看似简单却极为关键的问题正困扰着许多临床 AI 项目&#xff1a;如何让大模型真正“听懂”医生写的“心梗”“脑梗”“MI”其实是同一个病&#x…

作者头像 李华
网站建设 2026/4/30 13:36:12

TVM大语言模型优化终极指南:从量化到部署的完整解决方案

TVM大语言模型优化终极指南&#xff1a;从量化到部署的完整解决方案 【免费下载链接】tvm-cn TVM Documentation in Chinese Simplified / TVM 中文文档 项目地址: https://gitcode.com/gh_mirrors/tv/tvm-cn Apache TVM作为深度学习编译器领域的领先者&#xff0c;为大…

作者头像 李华
网站建设 2026/3/27 20:05:03

43、企业网络防火墙搭建与P2P服务管控指南

企业网络防火墙搭建与P2P服务管控指南 1. 网络接口基础设置 在Linux系统中,为了实现特定的网络功能,需要对网络接口进行一系列设置。首先,要对 /etc/sysconfig/network-scripts/ifcfg-eth0 和 /etc/sysconfig/network-scripts/ifcfg-eth1 进行配置,使它们具有相同的I…

作者头像 李华
网站建设 2026/5/1 8:49:13

终极指南:如何快速部署Qwen3-Coder实现智能编程助手

终极指南&#xff1a;如何快速部署Qwen3-Coder实现智能编程助手 【免费下载链接】Qwen3-Coder-30B-A3B-Instruct-FP8 项目地址: https://ai.gitcode.com/hf_mirrors/Qwen/Qwen3-Coder-30B-A3B-Instruct-FP8 你是否曾经在深夜调试代码时&#xff0c;希望有个AI助手能帮你…

作者头像 李华
网站建设 2026/5/1 8:49:04

LOOT工具完全指南:如何优化你的游戏模组加载顺序

LOOT工具完全指南&#xff1a;如何优化你的游戏模组加载顺序 【免费下载链接】loot A modding utility for Starfield and some Elder Scrolls and Fallout games. 项目地址: https://gitcode.com/gh_mirrors/lo/loot 为什么你的游戏模组总是崩溃&#xff1f; 如果你经…

作者头像 李华
网站建设 2026/4/29 19:37:08

DeepSeek-VL2:MoE架构引领多模态交互进入高效智能时代

导语 【免费下载链接】deepseek-vl2 探索视觉与语言融合新境界的DeepSeek-VL2&#xff0c;以其先进的Mixture-of-Experts架构&#xff0c;实现图像理解与文本生成的飞跃&#xff0c;适用于视觉问答、文档解析等多场景。三种规模模型&#xff0c;满足不同需求&#xff0c;引领多…

作者头像 李华