Java死锁案例排查与规避
死锁示例代码
public class DeadlockDemo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
// 线程1:先持有lock1,再获取lock2
Thread t1 = new Thread(() -> {
synchronized (lock1) {
System.out.println("线程1 持有 lock1");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("线程1 等待 lock2");
synchronized (lock2) {
System.out.println("线程1 获取 lock2");
}
}
});
// 线程2:先持有lock2,再获取lock1
Thread t2 = new Thread(() -> {
synchronized (lock2) {
System.out.println("线程2 持有 lock2");
try { Thread.sleep(100); } catch (InterruptedException e) {}
System.out.println("线程2 等待 lock1");
synchronized (lock1) {
System.out.println("线程2 获取 lock1");
}
}
});
t1.start();
t2.start();
}
}
死锁排查方法
使用jstack命令
# 1. 先找到Java进程ID jps -l # 2. 使用jstack分析线程堆栈 jstack -l <pid>
输出中会明确显示:

Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x000000001b3e4b28 (object 0x000000076b3e4b28, a java.lang.String),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x000000001b3e4b28 (object 0x000000076b3e4b28, a java.lang.String),
which is held by "Thread-1"
使用JConsole图形化工具
# 启动JConsole jconsole
步骤:
- 选择目标Java进程
- 点击"线程"标签
- 点击"检测死锁"按钮
- 查看死锁的线程及持有的锁
使用VisualVM
# 启动VisualVM jvisualvm
步骤:
- 选择目标进程
- 进入"线程"选项卡
- 查看线程状态,死锁线程会显示为红色
代码层面检测
public class DeadlockDetector {
public static void detectDeadlock() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
long[] threadIds = threadBean.findDeadlockedThreads();
if (threadIds != null) {
System.out.println("检测到死锁!");
ThreadInfo[] threadInfos = threadBean.getThreadInfo(threadIds);
for (ThreadInfo info : threadInfos) {
System.out.println("死锁线程: " + info.getThreadName());
System.out.println("等待的锁: " + info.getLockName());
System.out.println("持有的锁: " + Arrays.toString(info.getLockedMonitors()));
}
}
}
}
死锁规避策略
保持锁的顺序一致
public class SafeDeadlockDemo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
// 使用统一的锁获取顺序
public void safeMethod1() {
synchronized (lock1) {
synchronized (lock2) {
// 业务逻辑
}
}
}
public void safeMethod2() {
synchronized (lock1) { // 保持相同顺序
synchronized (lock2) {
// 业务逻辑
}
}
}
}
使用锁超时机制
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class TimeoutLockDemo {
private final Lock lock1 = new ReentrantLock();
private final Lock lock2 = new ReentrantLock();
public void safeMethod() {
try {
// 尝试获取lock1,超时时间1秒
if (lock1.tryLock(1, TimeUnit.SECONDS)) {
try {
// 尝试获取lock2,超时时间1秒
if (lock2.tryLock(1, TimeUnit.SECONDS)) {
try {
// 业务逻辑
System.out.println("获取所有锁成功");
} finally {
lock2.unlock();
}
} else {
System.out.println("获取lock2超时,释放lock1");
}
} finally {
lock1.unlock();
}
} else {
System.out.println("获取lock1超时");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
使用Java并发工具类
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.StampedLock;
public class ConcurrentToolDemo {
private final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
private final StampedLock stampedLock = new StampedLock();
public void safeMethod() {
// 使用StampedLock的乐观读
long stamp = stampedLock.tryOptimisticRead();
try {
// 业务逻辑
} finally {
if (!stampedLock.validate(stamp)) {
// 转换为悲观读锁
stamp = stampedLock.readLock();
try {
// 重新读取
} finally {
stampedLock.unlockRead(stamp);
}
}
}
}
}
使用数据库行级锁
// 使用数据库乐观锁避免死锁
public class DatabaseLockDemo {
@Autowired
private JdbcTemplate jdbcTemplate;
public void updateWithOptimisticLock(int id, int version) {
String sql = "UPDATE user SET name = ?, version = version + 1 " +
"WHERE id = ? AND version = ?";
int result = jdbcTemplate.update(sql, "newName", id, version);
if (result == 0) {
throw new OptimisticLockException("数据已被其他线程修改");
}
}
}
最佳实践建议
- 锁粒度控制:尽量使用细粒度锁,减少锁的持有范围
- 锁超时:使用
tryLock()方法设置超时时间 - 统一锁顺序:所有线程按相同顺序获取锁
- 减少锁的嵌套:尽量避免在持有锁的情况下获取其他锁
- 使用原子操作:如
AtomicInteger、ConcurrentHashMap等 - 定期检测:使用监控工具定期检查死锁情况
- 代码审查:特别是涉及多线程锁的代码
死锁检测工具对比
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| jstack | 命令行,快速 | 需要知道PID | 线上紧急排查 |
| JConsole | 图形化,直观 | 需要GUI环境 | 开发/测试环境 |
| VisualVM | 功能丰富 | 启动较慢 | 性能调优 |
| Java代码检测 | 可自动化 | 需要额外编码 | 监控系统集成 |
通过以上方法,可以有效排查和规避Java死锁问题。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播