zoukankan      html  css  js  c++  java
  • 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();
           }
       }
    
  • 相关阅读:
    SQL 生成可配置流水号
    安卓程序进入后台和前台的判断
    Android代码故事第一回,平均间隔的按钮
    安卓冷知识:LayoutParams
    初识Android NDK
    搬家
    LaTeX表格紧跟文字 (不影响下方文本对齐)
    FlagCounter被封杀?自己实现一个简单的多国访客计数器
    Python+OpenCV竖版古籍文字分割
    Ubuntu18.04 显卡驱动+Cuda安装踩坑记录 以及Ubuntu虚拟内存的添加
  • 原文地址:https://www.cnblogs.com/wangsen/p/11191001.html
Copyright © 2011-2022 走看看