什么是死锁
死锁出现的条件主要是资源互斥、占有并等待、非抢占、循环等待。
当出现两个线程对不同的资源进行获取的时候,A持有资源1,去获取资源2,B持有资源2,去获取资源1,就回出现死锁。
如何排查死锁
public class DealLock {
private Object objA = new Object();
private Object objB = new Object();
public void runA() throws InterruptedException {
synchronized (objA) {
System.out.println("获取到A锁,等待获取B锁");
Thread.sleep(1000);
synchronized (objB) {
System.out.println("A、B都获取到了");
}
}
}
public void runB() throws InterruptedException {
synchronized (objB) {
System.out.println("获取到A锁,等待获取B锁");
Thread.sleep(1000);
synchronized (objA) {
System.out.println("A、B都获取到了");
}
}
}
public static void main(String[] args) {
DealLock dealLock = new DealLock();
new Thread(()-> {
try {
dealLock.runA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()-> {
try {
dealLock.runB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fd4a801a000 nid=0x451f in Object.wait() [0x0000000306b2a000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000715588ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x0000000715588ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fd4a8013800 nid=0x3323 in Object.wait() [0x0000000306a27000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000715586bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x0000000715586bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=31 tid=0x00007fd49e014000 nid=0x1b03 waiting on condition [0x0000000305f06000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000007156964b8> (a java.util.concurrent.locks.ReentrantReadWriteLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
at java.util.concurrent.locks.ReentrantReadWriteLock$WriteLock.lock(ReentrantReadWriteLock.java:943)
at com.jia.lock.LockDemo2.main(LockDemo2.java:22)
区分线程状态 -> 查看等待目标 -> 对比 Monitor 等持有状态
如何预防死锁
其实就是预防、避免、检测和解除。
尽量使用 tryLock(long timeout, TimeUnit unit) 的方法 (ReentrantLock、 ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁;
尽量使用 Java. util. concurrent 并发类代替自己手写锁;
尽量降低锁的使用粒度,尽量不要几个功能用同一把锁;
尽量减少同步的代码块。
如果可能的话,尽量避免使用多个锁,并且只有需要时才持有锁
如果必须使用多个锁,尽量设计好锁的获取顺序