java死锁

什么是死锁

简单说:
有一个线程A,按照先获取锁a再获得锁b的的顺序获得锁,
而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁,
这个时候因为两个线程都在等待彼此手里的锁而形成了死锁。
如图:

死锁产生的四个条件

  • 互斥条件
    进程持有的资源,保证同一时间内只能有一个线程持有。
  • 不剥夺条件
    进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
  • 请求和保持条件
    进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此是请求阻塞,但是它又对自己获得的资源保持不放。
  • 环路等待条件
    若干进程间形成首尾相接循环等待资源的关系。

手写一个死锁

public class TestDeaLock {
   private Lock lockA=new ReentrantLock();
   private Lock lockB=new ReentrantLock();

   public  void method1(){
       try {
           lockA.lock();
           //这里假设执行业务逻辑,需要执行1s,这个时间线程2获取到了lockB
           Thread.sleep(1000);
           System.out.println(Thread.currentThread().getName()+"获取A锁!开始尝试获取B锁.......");
           lockB.lock();
           System.out.println(Thread.currentThread().getName()+"获取B锁!");
       } catch (Exception e) {
           e.printStackTrace();
       }finally {
           lockA.unlock();
           lockB.unlock();
       }
   }

   public  void method2(){
       try {
           lockB.lock();
           System.out.println(Thread.currentThread().getName()+"获取B锁!开始尝试获取A锁.....");
           lockA.lock();
           System.out.println(Thread.currentThread().getName()+"获取A锁!");
       } finally {
           lockB.unlock();
           lockA.unlock();
       }
   }

    public static void main(String[] args) {
        TestDeaLock tdl=new TestDeaLock();
        new Thread(()->{
            tdl.method1();
        }).start();
        new Thread(()->{
            tdl.method2();
        }).start();

    }
}

运行结果:

Thread-1获取B锁!开始尝试获取A锁.....
Thread-0获取A锁!开始尝试获取B锁.......

怎么解决死锁

我们就拿上面的手写锁代码进行排查。

使用jps命令定位进程号

14000 sun.tools.jps.Jps
16672 org.jetbrains.jps.cmdline.Launcher
13076
29512 com.ws.TestDeaLock
4376

从进程号中可以看出运行程序的进程号:29512

使用jstack命令找到死锁查看

使用命令:jstack 29512

部分内容...

Found one Java-level deadlock:
=============================
"Thread-1":
  waiting for ownable synchronizer 0x000000076b5042d8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-0"
"Thread-0":
  waiting for ownable synchronizer 0x000000076b504308, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1":
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000076b5042d8> (a java.util.concurrent.locks.ReentrantLock$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.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
        at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
        at com.ws.TestDeaLock.mothod2(TestDeaLock.java:41)
        at com.ws.TestDeaLock.lambda$main$1(TestDeaLock.java:55)
        at com.ws.TestDeaLock$$Lambda$2/1348949648.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)
"Thread-0":
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x000000076b504308> (a java.util.concurrent.locks.ReentrantLock$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.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
        at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
        at com.ws.TestDeaLock.mothod1(TestDeaLock.java:27)
        at com.ws.TestDeaLock.lambda$main$0(TestDeaLock.java:52)
        at com.ws.TestDeaLock$$Lambda$1/736709391.run(Unknown Source)
        at java.lang.Thread.run(Thread.java:745)

Found 1 deadlock.

从堆栈信息里可以查看出jvm已经帮我们找到了一个死锁:
线程Thread-1在等待获取0x000000076b5042d8,这个锁被Thread-0占有,
Thread-0在等待获取0x000000076b504308,这个锁被Thead-1占有,这就形成了环路等待,造成了死锁。

  • 可以结束这个进程

怎么避免死锁

  • 在开发时需要慎重使用锁,需要注意尽量不要在已经获取到锁的情况下,再去尝试获取其他的锁。
  • 获取锁的时候可以使用超时放弃(lock提供的方法)
  • 调整申请锁的顺序
    比如上面的死锁,method2可以调整为:
 public  void method2(){
       try {
           //这里获取锁的顺序和method1的顺序相同就可以避免
           lockA.lock();
           System.out.println(Thread.currentThread().getName()+"获取A锁!");
           lockB.lock();
           System.out.println(Thread.currentThread().getName()+"获取B锁!开始尝试获取A锁.....");
       } finally {
           lockB.unlock();
           lockA.unlock();
       }
   }
原文地址:https://www.cnblogs.com/wangsen/p/11191001.html