Java中常见死锁与活锁的实例详解

 更新时间:2018年11月26日 11:37:20   作者:爬蜥  
这篇文章主要介绍了Java中常见死锁与活锁的实例详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

本文介绍了Java中常见死锁与活锁的实例详解,分享给大家,具体如下:

  • 顺序死锁:过度加锁,导致由于执行顺序的原因,互相持有对方正在等待的锁
  • 资源死锁:多个线程在相同的资源上发生等待

由于调用顺序而产生的死锁

public class Test {
  Object leftLock = new Object();
  Object rightLock = new Object();
  public static void main(String[] args) {
    final Test test = new Test();
    Thread a = new Thread(new Runnable() {
      @Override      public void run() {
        int i=0;
        while (i<10)
        {
          test.leftRight();
          i++;
        }
      }
    },"aThread");
    Thread b = new Thread(new Runnable() {
      @Override      public void run() {
        int i=0;
        while (i<10)
        {
          test.rightleft();
          i++;
        }
      }
    },"bThread");
    a.start();
    b.start();
  }

  public void leftRight(){
    synchronized (leftLock){
      System.out.println(Thread.currentThread().getName()+":leftRight:get left");
      synchronized (rightLock){
        System.out.println(Thread.currentThread().getName()+":leftRight:get right");
      }
    }
  }

  public void rightleft(){
    synchronized (rightLock){
      System.out.println(Thread.currentThread().getName()+":rightleft: get right");
      synchronized (leftLock){
        System.out.println(Thread.currentThread().getName()+":rightleft: get left");
      }
    }
  }

}

运行后输出如下

aThread:leftRight:get left
bThread:rightleft: get right

可以通过jstack发现死锁的痕迹

"bThread" prio=5 tid=0x00007fabb2001000 nid=0x5503 waiting for monitor entry [0x000000011d54b000]
  java.lang.Thread.State: BLOCKED (on object monitor)
  at main.lockTest.Test.rightleft(Test.java:52)
  - waiting to lock <0x00000007aaee5748> (a java.lang.Object)
  - locked <0x00000007aaee5758> (a java.lang.Object)
  at main.lockTest.Test$2.run(Test.java:30)
  at java.lang.Thread.run(Thread.java:745)

  Locked ownable synchronizers:
  - None

"aThread" prio=5 tid=0x00007fabb2801000 nid=0x5303 waiting for monitor entry [0x000000011d448000]
  java.lang.Thread.State: BLOCKED (on object monitor)
  at main.lockTest.Test.leftRight(Test.java:43)
  - waiting to lock <0x00000007aaee5758> (a java.lang.Object)
  - locked <0x00000007aaee5748> (a java.lang.Object)
  at main.lockTest.Test$1.run(Test.java:19)
  at java.lang.Thread.run(Thread.java:745)

  Locked ownable synchronizers:
  - None

可以看到bThread持有锁0x00000007aaee5758,同时等待0x00000007aaee5748,然而恰好aThread持有锁0x00000007aaee5748并等待0x00000007aaee5758,从而形成了死锁

线程饥饿死锁

public class ExecutorLock {
  private static ExecutorService single=Executors.newSingleThreadExecutor();
  public static class AnotherCallable implements Callable<String>{

    @Override    public String call() throws Exception {
      System.out.println("in AnotherCallable");
      return "annother success";
    }
  }


  public static class MyCallable implements Callable<String>{

    @Override    public String call() throws Exception {
      System.out.println("in MyCallable");
      Future<String> submit = single.submit(new AnotherCallable());
      return "success:"+submit.get();
    }
  }
  public static void main(String[] args) throws ExecutionException, InterruptedException {
    MyCallable task = new MyCallable();
    Future<String> submit = single.submit(task);
    System.out.println(submit.get());
    System.out.println("over");
    single.shutdown();
  }
}

执行的输出只有一行

in MyCallable

通过jstack观察可以看到如下

"main" prio=5 tid=0x00007fab3f000000 nid=0x1303 waiting on condition [0x0000000107d63000]
  java.lang.Thread.State: WAITING (parking)
  at sun.misc.Unsafe.park(Native Method)
  - parking to wait for <0x00000007aaeed1d8> (a java.util.concurrent.FutureTask)
  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
  at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:425)
  at java.util.concurrent.FutureTask.get(FutureTask.java:187)
  at main.lockTest.ExecutorLock.main(ExecutorLock.java:32)
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:606)
  at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

  Locked ownable synchronizers:
  - None
..
"pool-1-thread-1" prio=5 tid=0x00007fab3f835800 nid=0x5303 waiting on condition [0x00000001199ee000]
  java.lang.Thread.State: WAITING (parking)
  at sun.misc.Unsafe.park(Native Method)
  - parking to wait for <0x00000007ab0f8698> (a java.util.concurrent.FutureTask)
  at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
  at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:425)
  at java.util.concurrent.FutureTask.get(FutureTask.java:187)
  at main.lockTest.ExecutorLock$MyCallable.call(ExecutorLock.java:26)
  at main.lockTest.ExecutorLock$MyCallable.call(ExecutorLock.java:20)
  at java.util.concurrent.FutureTask.run(FutureTask.java:262)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
  at java.lang.Thread.run(Thread.java:745)

  Locked ownable synchronizers:
  - <0x00000007aaeed258> (a java.util.concurrent.ThreadPoolExecutor$Worker)

主线程在等待一个FutureTask完成,而线程池中一个线程也在等待一个FutureTask完成。
从代码实现可以看到,主线程往线程池中扔了一个任务A,任务A又往同一个线程池中扔了一个任务B,并等待B的完成,由于线程池中只有一个线程,这将导致B会被停留在阻塞队列中,而A还得等待B的完成,这也就是互相等待导致了死锁的反生

这种由于正在执行的任务线程都在等待其它工作队列中的任务而阻塞的现象称为 线程饥饿死锁

活锁

并未产生线程阻塞,但是由于某种问题的存在,导致无法继续执行的情况。

1、消息重试。当某个消息处理失败的时候,一直重试,但重试由于某种原因,比如消息格式不对,导致解析失败,而它又被重试

这种时候一般是将不可修复的错误不要重试,或者是重试次数限定

2、相互协作的线程彼此响应从而修改自己状态,导致无法执行下去。比如两个很有礼貌的人在同一条路上相遇,彼此给对方让路,但是又在同一条路上遇到了。互相之间反复的避让下去

这种时候可以选择一个随机退让,使得具备一定的随机性

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • HashMap之keyset()方法底层原理解读

    HashMap之keyset()方法底层原理解读

    这篇文章主要介绍了HashMap之keyset()方法底层原理解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-03-03
  • 详解Java数据结构之平衡二叉树

    详解Java数据结构之平衡二叉树

    平衡二叉树(Balanced Binary Tree)又被称为AVL树(有别于AVL算法),且具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。本文将详解介绍一下平衡二叉树的原理与实现,需要的可以参考一下
    2022-02-02
  • java中使用dom4j解析XML文件的方法教程

    java中使用dom4j解析XML文件的方法教程

    在最近的开发中用到了dom4j来解析xml文件,所以便有了这篇文章,本文主要给大家介绍了关于java中使用dom4j解析XML文件的方法教程,文中通过示例代码介绍的非常详细,需要的朋友可以参考下
    2018-09-09
  • Spring导入properties配置文件代码示例

    Spring导入properties配置文件代码示例

    这篇文章主要介绍了Spring导入properties配置文件代码示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Java 实现浏览器下载文件及文件预览

    Java 实现浏览器下载文件及文件预览

    这篇文章主要介绍了Java 实现浏览器下载文件及文件预览,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • 通过实例学习JAVA对象转成XML输出

    通过实例学习JAVA对象转成XML输出

    这篇文章主要介绍了通过实例学习JAVA对象转成XML输出,做流程图的项目时,新的流程定义为xml的,需要对xml与java对象进行互转,下面我们来深入学习,需要的朋友可以参考下
    2019-06-06
  • Spring Boot 中实现跨域的多种方式小结

    Spring Boot 中实现跨域的多种方式小结

    Spring Boot提供了多种方式来实现跨域请求,开发者可以根据具体需求选择适合的方法,在配置时,要确保不仅考虑安全性,还要兼顾应用的灵活性和性能,本文给大家介绍Spring Boot 中实现跨域的多种方式,感兴趣的朋友一起看看吧
    2024-01-01
  • Spring框架中IoC容器与DI依赖注入教程

    Spring框架中IoC容器与DI依赖注入教程

    IOC也是Spring的核心之一了,之前学的时候是采用xml配置文件的方式去实现的,后来其中也多少穿插了几个注解,但是没有说完全采用注解实现。那么这篇文章就和大家分享一下,全部采用注解来实现IOC + DI
    2023-01-01
  • 详解如何获取PreparedStatement参数示例详解

    详解如何获取PreparedStatement参数示例详解

    这篇文章主要为大家介绍了详解如何获取PreparedStatement参数示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • Java实现简易学生管理系统

    Java实现简易学生管理系统

    这篇文章主要为大家详细介绍了Java实现简易学生管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-07-07

最新评论