Deadlock

[color=red][b]Deadlock describes a situation where two or more threads are blocked forever, waiting for each other. [/b][/color]

package Cocurrent;

public class Deadlock {
static class Friend {
private final String name;
public Friend(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
/**
* 向friend鞠躬
* @param bower
*/
public synchronized void bow(Friend friend) {
//用println并不能触发死锁的情况,因为第二个线程起来的时候,第一个已经执行完了。
// System.out.println(this.getName() + " bowed to "+friend.getName());
System.out.format("%s has bowed to %s!%n",
this.name, friend.getName());
friend.bowBack(this);
}
/**
* 向friend回礼
* @param bower
*/
public synchronized void bowBack(Friend friend) {
// System.out.println(this.getName() + " bowed back to "+friend.getName());
System.out.format("%s has bowed back to %s!%n",
this.name, friend.getName());
}
}

public static void main(String[] args) {
final Friend alphonse =
new Friend("Alphonse");
final Friend gaston =
new Friend("Gaston");
new Thread(new Runnable() {
public void run() { alphonse.bow(gaston); }
}).start();
new Thread(new Runnable() {
public void run() { gaston.bow(alphonse); }
}).start();
}
}


分析和结果
第一个线程起来,获取A的锁,A向G鞠躬,等待G的锁,然后G执行回礼
第二个线程起来,获取G的锁,G向A鞠躬,等待A的锁,然后A执行回礼
但A和G的方法都无法退出,互相等待。
因此造成了死锁
执行结果:
Gaston has bowed to Alphonse!
Alphonse has bowed to Gaston!

程序曾用println代替,但是发现没有触发死锁的条件,必须A向G鞠躬的同时并且未从该方法中退出,G向A鞠躬,才触发。
### 诊断应用程序中明显的死锁问题(Apparent Deadlock in Application Troubleshooting) 在多线程应用程序中,死锁是一种常见的并发问题,通常表现为多个线程互相等待对方持有的资源,导致程序无法继续执行。检测和解决死锁问题需要结合线程堆栈分析、资源持有关系追踪以及日志信息的综合判断。 #### 死锁的典型特征 在堆栈跟踪中,可以观察到多个线程处于 `BLOCKED` 或 `WAITING` 状态,并且它们正在等待彼此持有的锁。例如,一个线程 A 持有资源 X 并尝试获取资源 Y,而线程 B 持有资源 Y 并尝试获取资源 X,形成一个循环依赖,导致死锁发生。 #### 死锁检测方法 ##### 1. 使用线程转储工具(Thread Dump) 在 Java 应用程序中,可以通过 `jstack` 工具生成线程转储,分析线程之间的锁依赖关系。以下是一个典型的线程阻塞示例: ```bash "Thread-1" #11 prio=5 os_prio=0 tid=0x00007f8c4c0d3800 nid=0x5e5d waiting for monitor entry [0x00007f8c479d8000] java.lang.Thread.State: BLOCKED (on object monitor) at com.example.DeadlockExample.methodB(DeadlockExample.java:25) - waiting to lock <0x000000076ab002a0> (a java.lang.Object) - locked <0x000000076ab002b0> (a java.lang.Object) ``` 上述输出显示线程 Thread-1 正在等待一个被其他线程持有的锁,同时它已经持有了另一个锁。通过分析多个线程的堆栈信息,可以识别出循环依赖关系,从而确认是否存在死锁。 ##### 2. 使用 JVM 内置工具(如 JConsole 或 VisualVM) 这些工具可以图形化地展示线程状态、锁信息以及线程之间的依赖关系。VisualVM 还可以自动检测死锁并高亮显示相关线程。 ##### 3. 日志分析 在应用程序中启用详细的日志记录,特别是与锁获取和释放相关的操作。通过日志可以追踪线程在何时何地尝试获取锁,以及是否有异常延迟或阻塞情况。 ##### 4. 数据库级别的死锁监控 在数据库操作中,死锁也可能导致应用程序线程阻塞。例如,在 SQL Server 中,死锁可以通过 `sys.dm_exec_requests` 和 `sys.dm_os_waiting_tasks` 等系统视图进行监控。此外,SQL Server Profiler 可以捕获死锁事件并生成 XML 报告,用于进一步分析[^1]。 ##### 5. 锁类型分析 常见的锁类型包括: - **RID**:单行锁 - **KEY**:索引键范围锁 - **PAG**:数据或索引页锁 - **EXT**:区段锁 - **TAB**:表锁 - **DB**:数据库锁 不同类型的锁可能影响并发性能和死锁发生的概率。通过分析数据库事务的隔离级别和查询执行计划,可以优化锁的使用,减少死锁风险[^2]。 #### 死锁的预防与解决策略 - **避免嵌套锁**:尽量避免在同一个线程中连续获取多个锁。 - **按顺序获取锁**:确保所有线程以相同的顺序获取锁,从而避免循环依赖。 - **使用超时机制**:在尝试获取锁时设置超时时间,避免无限期等待。 - **减少事务持有锁的时间**:缩短事务执行时间,尽早释放锁资源。 - **使用乐观锁机制**:如版本号控制,减少对悲观锁的依赖。 #### 示例代码:检测死锁的 Java 程序 ```java public class DeadlockExample { private static final Object lock1 = new Object(); private static final Object lock2 = new Object(); public static void main(String[] args) { new Thread(() -> { synchronized (lock1) { System.out.println("Thread 1: Holding lock 1..."); try { Thread.sleep(100); } catch (InterruptedException e) {} System.out.println("Thread 1: Waiting for lock 2..."); synchronized (lock2) { System.out.println("Thread 1: Acquired lock 2..."); } } }).start(); new Thread(() -> { synchronized (lock2) { System.out.println("Thread 2: Holding lock 2..."); try { Thread.sleep(100); } catch (InterruptedException e) {} System.out.println("Thread 2: Waiting for lock 1..."); synchronized (lock1) { System.out.println("Thread 2: Acquired lock 1..."); } } }).start(); } } ``` 运行该程序后,使用 `jstack` 分析线程状态,可以清晰地看到两个线程相互等待对方持有的锁,形成死锁。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值