多线程——同步嵌套造成死锁问题

本文通过一个Java示例分析了多线程同步嵌套可能引发的死锁状况。建议尽量避免同步嵌套以防止死锁,同时展示了如何在代码中设置标志来控制线程行为以减少死锁风险。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.qianfeng.demo02;
/**
 * 同步的另外一个弊端:死锁
 * 
 * 最常见的死锁的情况:
 * 同步嵌套,同步中还有同步,然后两个同步用的不是一把锁。
 * 
 * 不是使用死锁,而是避免死锁。
 * 
 * 尽量避免同步嵌套的情况。
 * */
public class DeadLockDemo01 {

	public static void main(String[] args) {
		SaleTicket st = new SaleTicket();
		
		Thread t0 = new Thread(st);
		Thread t1 = new Thread(st);
		
		t0.start();
		
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		st.setFlag(false);
		t1.start();
	}
}

class SaleTicket implements Runnable{
	private int num = 100;
	Object obj = new Object();
	private boolean flag = true;
	public void setFlag(boolean flag) {
		this.flag = flag;
	}
	public boolean isFlag() {
		return flag;
	}
	@Override
	public void run() {
		if (flag) {
			while (true) {
				synchronized (obj) {
					show();
				}
			}
		}else {
			while (true) {
				show();       //出现同步的嵌套,可能会产生死锁
			}
		}
	}
	
	public synchronized void show(){
		synchronized (obj) {
			if (num>0) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName()+"...sale.."+num--);
			}
		}
	}
}

<think>嗯,用户现在聚焦于死锁预防这个关键问题,而且明确要求参考站内引用内容。从对话历史看,用户之前已经接受了关于线程安全的系统讲解,现在需要深入死锁这个子领域。 用户提供的引用材料很有价值:[1][2]从概念层面定义死锁,[3][4]给出具体策略,[5]那个哲学家就餐问题的代码示例特别生动。不过要注意用户特别强调"不是真实需求",说明这些引用只是知识背景,核心还是解决"如何防止死锁"这个实际问题。 用户身份可能是中高级开发者——能提出这么具体的技术问题,还带着引用文献,显然不是新手。但ta没直接贴自己的代码,说明可能需要通用方案而非调试帮助。情绪上显得冷静专注,应该期待专业级解答。 死锁的四个必要条件得先点明,这是所有预防策略的根基。引用[3]提到的锁顺序和引用[4]的try_lock都是重点,但需要融合:比如把锁排序作为主要手段,try_lock作为补充方案。那个哲学家示例[5]可以转化成预防案例,比单纯讲理论更有说服力。 特别要注意避免教条。比如有人说"永远不用嵌套锁",但实际开发中嵌套有时不可避免,这时候就需要引用[4]说的"固定顺序"技巧。性能方面也得提一嘴,像try_lock的重试机制虽然安全但可能消耗CPU。 最后结构化呈现:先讲原则(锁顺序/超时),再给代码示例(Java版哲学家问题改造),最后升华到设计层面(如无锁数据结构)。这样从理论到实践都覆盖了,正好回应用户引用的所有材料。</think>在多线程编程中防止死锁的核心在于**破坏死锁产生的必要条件**。死锁需同时满足四个条件:互斥、持有并等待、不可抢占、循环等待。以下是关键预防策略及实践方法: --- ### 一、破坏循环等待:固定锁的获取顺序 **原理**:若所有线程按全局统一顺序获取锁,循环等待链将被打破。 **示例**(改造自引用[5]的哲学家就餐问题): ```java // 定义全局锁顺序:先筷子后勺子 final Object firstLock = new Object(); final Object secondLock = new Object(); Thread threadA = new Thread(() -> { synchronized (firstLock) { // 固定先获取 firstLock synchronized (secondLock) { // 再获取 secondLock // 操作共享资源 } } }); Thread threadB = new Thread(() -> { synchronized (firstLock) { // 同样先获取 firstLock synchronized (secondLock) { // 再获取 secondLock // 操作共享资源 } } }); ``` > **效果**:强制所有线程按`firstLock → secondLock`顺序申请锁,避免交叉等待[^3][^4]。 --- ### 二、破坏持有并等待:原子化锁获取 **原理**:通过一次性获取所有所需锁,避免持有部分锁时等待其他锁。 **实现方式**: 1. **使用`ReentrantLock.tryLock()`** 尝试获取所有锁,失败则释放已持有锁并重试(引用[4]): ```java ReentrantLock lock1 = new ReentrantLock(); ReentrantLock lock2 = new ReentrantLock(); while (true) { if (lock1.tryLock()) { try { if (lock2.tryLock()) { // 尝试获取第二把锁 try { // 执行操作 break; // 成功则退出循环 } finally { lock2.unlock(); } } } finally { lock1.unlock(); } // 失败则释放第一把锁 } Thread.sleep(100); // 避免CPU空转 } ``` 2. **锁管理器(Lock Manager)** 设计中央锁服务,统一处理锁请求并保证原子性分配。 --- ### 三、破坏不可抢占:支持锁超时与中断 **原理**:允许线程主动放弃已持有锁,打破僵持状态。 **实践**: - **设置锁超时**:`synchronized`不支持超时,优先使用`ReentrantLock`: ```java if (lock.tryLock(1, TimeUnit.SECONDS)) { // 1秒超时 try { /* 操作资源 */ } finally { lock.unlock(); } } else { // 超时处理:回滚或重试 } ``` - **响应中断**:调用`lockInterruptibly()`允许外部中断等待[^4]。 --- ### 四、设计层面优化 1. **减少锁粒度** 使用细粒度锁(如`ConcurrentHashMap`的分段锁)代替全局锁,降低竞争概率[^3]。 2. **避免嵌套锁** 尽量不在持有一个锁时申请另一个锁(引用[4]),必要时使用上述原子化方案。 3. **无锁编程** 使用原子类(`AtomicInteger`等)或不可变对象替代锁: ```java AtomicReference<Data> ref = new AtomicReference<>(); ref.updateAndGet(old -> new Data(/*更新后的值*/)); // CAS操作保证安全 ``` 4. **死锁检测与恢复** 监控线程等待图(如JDK的`jstack`),检测到循环等待时强制释放资源(生产环境慎用)。 --- ### 死锁场景复现与解决(引用[5]修正) ```java // 原死锁代码:小美先拿筷子后拿勺,小帅先拿勺后拿筷子 → 循环等待 // 修正:统一先拿筷子再拿勺 Thread xiaomei = new Thread(() -> { synchronized (chopsticks) { // 固定顺序:先筷子 synchronized (spoon) { // 后勺子 System.out.println("小美吃饭"); } } }); Thread xiaoshuai = new Thread(() -> { synchronized (chopsticks) { // 同样先筷子 synchronized (spoon) { // 后勺子 System.out.println("小帅吃饭"); } } }); ``` --- ### 最佳实践总结 | **策略** | **实现方式** | **适用场景** | |------------------------|----------------------------------|--------------------------| | **固定锁顺序** | 全局定义锁获取顺序 | 锁数量少且固定 | | **原子化锁获取** | `tryLock()`组合/锁管理器 | 需要动态获取多个锁 | | **超时与中断** | `tryLock(timeout)`/`lockInterruptibly()` | 高实时性要求 | | **无锁数据结构** | 原子类、不可变对象 | 计数器、状态标志等简单操作 | | **减小临界区** | 缩小同步代码块范围 | 任何涉及锁的代码 | > 关键原则:**按顺序、少嵌套、短持有、可中断**。通过代码审查和压力测试(如`jcstress`)验证死锁预防有效性[^1][^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值