Java 死锁排查
通过 jps + jstack 来定位和排查
如果线程长时间处于阻塞,就需要考虑是否是死锁了。
模拟死锁
public class DeadlockDemo {private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {// 创建线程1Thread t1 = new Thread(() -> {synchronized (lock1) {System.out.println("Thread 1: Holding lock 1...");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1: Waiting for lock 2...");synchronized (lock2) {System.out.println("Thread 1: Holding lock 1 & 2...");}}});// 创建线程2Thread t2 = new Thread(() -> {synchronized (lock2) {System.out.println("Thread 2: Holding lock 2...");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2: Waiting for lock 1...");synchronized (lock1) {System.out.println("Thread 2: Holding lock 1 & 2...");}}});// 启动线程t1.start();
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }t2.start();}
}
Thread 1: Holding lock 1...
Thread 2: Holding lock 2...
Thread 1: Waiting for lock 2...
Thread 2: Waiting for lock 1...
通过 jps + jstack 来定位和排查
PS D:\xxx> jps 查看java进程状态
19504
16356 RemoteMavenServer36
20692 Launcher
9044 DeadlockDemo
13064 Jps
PS D:\xxx> jstack 9044 打印java进程堆栈
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x0000000026a08df8 (object 0x00000007174cc1b8, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x0000000026a0b898 (object 0x00000007174cc1c8, a java.lang.Object),
which is held by "Thread-1"Java stack information for the threads listed above:
===================================================
"Thread-1":
at DeadlockDemo.lambda$main$1(DeadlockDemo.java:33)
- waiting to lock <0x00000007174cc1b8> (a java.lang.Object)
- locked <0x00000007174cc1c8> (a java.lang.Object)
at DeadlockDemo$$Lambda$2/1078694789.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-0":
at DeadlockDemo.lambda$main$0(DeadlockDemo.java:17)
- waiting to lock <0x00000007174cc1c8> (a java.lang.Object)
- locked <0x00000007174cc1b8> (a java.lang.Object)
at DeadlockDemo$$Lambda$1/1324119927.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)Found 1 deadlock.
预防和处理死锁
1.考虑顺序获取需要的资源;
2.考虑无锁的方案;
MySQL死锁排查
使用 SHOW ENGINE INNODB STATUS # 查看 死锁日志
模拟死锁
建表语句
-- 创建users表
CREATE TABLE users (id INT PRIMARY KEY, -- 主键,唯一标识用户name VARCHAR(50) NOT NULL, -- 用户名称balance DECIMAL(10, 2) -- 用户余额
);-- 为name字段创建普通索引
CREATE INDEX idx_name ON users (name);-- 插入测试数据
INSERT INTO users (id, name, balance) VALUES
(1, 'Bob', 1000.00), -- 用户Bob,余额1000
(2, 'Alice', 1000.00); -- 用户Alice,余额1000UPDATE `users`
set balance = 1000;
事务1
-- 会话1
BEGIN;
UPDATE users SET balance = balance - 100 WHERE name = 'Bob'; -- 更新Bob的余额
UPDATE users SET balance = balance + 100 WHERE name = 'Alice'; -- 更新COMMIT;
事务2
-- 会话2
BEGIN;
UPDATE users SET balance = balance + 100 WHERE name = 'Alice'; -- 更新Alice的余额
UPDATE users SET balance = balance - 100 WHERE name = 'Bob'; -- 更新Bob的余额COMMIT;
死锁检测设置
show variables like 'innodb_deadlock_detect';set global innodb_deadlock_detect = 1; # 开启死锁检测 如有死锁 会回滚某一个事务
set global innodb_deadlock_detect = 0;SHOW ENGINE INNODB STATUS # 查看 死锁日志
首先关闭死锁检测
执行事务1 的前2条语句, 执行事务2, 会发现事务2 阻塞住了
SHOW ENGINE INNODB STATUS # 查看 死锁日志
开启死锁检测
预防和处理MySQL死锁
1. 统一资源访问顺序;
2.减少事务执行时间,长事务的死锁概率更高;
3.选择合适的隔离级别,如果不需要保证可重复读,可以选择读取已提交;
可重复读可能会产生 间隙锁,增大死锁的概率。