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

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

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

(0)

相关推荐

  • java 多线程死锁详解及简单实例

    java 多线程死锁 相信有过多线程编程经验的朋友,都吃过死锁的苦.除非你不使用多线程,否则死锁的可能性会一直存在.为什么会出现死锁呢?我想原因主要有下面几个方面: (1)个人使用锁的经验差异     (2)模块使用锁的差异     (3)版本之间的差异     (4)分支之间的差异     (5)修改代码和重构代码带来的差异 不管什么原因,死锁的危机都是存在的.那么,通常出现的死锁都有哪些呢?我们可以一个一个看过来,     (1)忘记释放锁 void data_process() { Ent

  • Java多线程之死锁的出现和解决方法

    什么是死锁? 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不能正常运行.形象的说就是:一个宝藏需要两把钥匙来打开,同时间正好来了两个人,他们一人一把钥匙,但是双方都再等着对方能交出钥匙来打开宝藏,谁都没释放自己的那把钥匙.就这样这俩人一直僵持下去,直到开发人员发现这个局面. 导致死锁的根源在于不适当地运用"synchronized"关键词来管理线程对特定对象的访问."synchronized"关

  • java避免死锁的常见方法代码解析

    死锁 索是一个非常有用的工具,运用场景非常多,因为它使用起来非常简单,而且易于理解.但同时它也会带来一些困扰,那就是可能会引起死锁,一旦产生死锁,就会造成系统功能不可用.让我们先来看一段代码,这段代码会引起死锁,使线程 thread_1 和线程 thread_2 互相等待对方释放锁. package thread; public class DeadLockDemo { private static String A = "A"; private static String B = &

  • Java多线程产生死锁的必要条件

    线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行.当线程进入对象的synchronized代码块时,便占有了资源,直到它退出该代码块或者调用wait方法,才释放资源,在此期间,其他线程将不能进入该代码块.当线程互相持有对方所需要的资源时,会互相等待对方释放资源,如果线程都不主动释放所占有的资源,将产生死锁. 死锁是操作系统里里面的一个重要的概念,死锁通常发生在并发的场景里. 死锁是多个进程或线程,彼此争抢资源而陷入僵局的一种情况. 在笔者参加的多次

  • java多线程学习之死锁的模拟和避免(实例讲解)

    1.死锁 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不可能正常终止. Java 死锁产生的四个必要条件: 1.互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 2.不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放. 3.请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有. 4.循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的

  • java实现死锁的示例代码

    什么是死锁 我们先看看这样一个生活中的例子:在一条河上有一座桥,桥面较窄,只能容纳一辆汽车通过,无法让两辆汽车并行.如果有两辆汽车A和B分别由桥的两端驶上该桥,则对于A车来说,它走过桥面左面的一段路(即占有了桥的一部分资源),要想过桥还须等待B车让出右边的桥面,此时A车不能前进:对于B车来说,它走过桥面右边的一段路(即占有了桥的一部分资源),要想过桥还须等待A车让出左边的桥面,此时B车也不能前进.两边的车都不倒车,结果造成互相等待对方让出桥面,但是谁也不让路,就会无休止地等下去.这种现象就是死锁

  • Java多线程死锁示例

    本文实例演示了Java多线程死锁.分享给大家供大家参考,具体如下: package com.damlab.fz; public class DeadLock { public static void main(String[] args) { Resource r1 = new Resource(); Resource r2 = new Resource(); // 每个线程都拥有r1,r2两个对象 Thread myTh1 = new MyThread1(r1, r2); Thread myT

  • 详解Java的线程的优先级以及死锁

    Java线程优先级 需要避免的与多任务处理有关的特殊错误类型是死锁(deadlock).死锁发生在当两个线程对一对同步对象有循环依赖关系时.例如,假定一个线程进入了对象X的管程而另一个线程进入了对象Y的管程.如果X的线程试图调用Y的同步方法,它将像预料的一样被锁定.而Y的线程同样希望调用X的一些同步方法,线程永远等待,因为为到达X,必须释放自己的Y的锁定以使第一个线程可以完成.死锁是很难调试的错误,因为: 通常,它极少发生,只有到两线程的时间段刚好符合时才能发生. 它可能包含多于两个的线程和同步

  • Java 解决死锁的方法实例详解

    死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不可能正常终止. java 死锁产生的四个必要条件: 1>互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 2>不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放. 3>请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的战友. 4>循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P

  • 详解Java中synchronized关键字的死锁和内存占用问题

    先看一段synchronized 的详解: synchronized 是 java语言的关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码. 一.当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行.另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块. 二.然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以

随机推荐