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

作者:爬蜥 时间:2023-06-26 13:17:00 

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

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

来源:https://segmentfault.com/a/1190000017134766

标签:Java,死锁,活锁
0
投稿

猜你喜欢

  • C#实现多个计时器记录不同定时时间

    2023-01-31 17:56:33
  • 详解如何将JAR包发布到Maven中央仓库

    2023-02-14 07:26:00
  • C# pictureBox用法案例详解

    2022-02-24 19:40:07
  • 基于SpringMVC入门案例及讲解

    2023-04-06 17:34:43
  • SpringBoot整合Apollo配置中心快速使用详解

    2022-12-25 17:00:34
  • spring boot使用自定义的线程池执行Async任务

    2023-08-15 07:41:25
  • 详解Java分布式事务的 6 种解决方案

    2022-06-19 13:33:49
  • Android Retrofit 2.0框架上传图片解决方案

    2022-02-23 06:25:27
  • 在android中如何用Java加载解析so

    2023-09-14 18:16:05
  • android studio 3.6 中配置svn的教程

    2022-02-26 20:59:06
  • Java thrift服务器和客户端创建实例代码

    2022-01-23 04:38:29
  • 通俗易通讲解Android蓝牙键值适配

    2022-06-19 07:50:34
  • Android自定义TextView跑马灯效果

    2023-08-07 01:14:57
  • MongoDB中ObjectId的误区及引起的一系列问题

    2023-06-16 09:46:00
  • Java8中 LocalDate和java.sql.Date的相互转换操作

    2022-01-05 20:01:28
  • 超详细的Java 问题排查工具单

    2023-02-10 06:02:17
  • Android中获取资源 id 及资源 id 的动态获取

    2023-06-30 04:38:06
  • SSH原理及两种登录方法图文详解

    2023-11-14 11:10:53
  • C#编程简单实现生成PDF文档的方法示例

    2023-09-26 05:36:32
  • C#线程中弹窗的制作方法

    2023-08-14 03:25:32
  • asp之家 软件编程 m.aspxhome.com