Java死锁_动力节点Java学院整理

死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源。在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它。

Java死锁范例

ThreadDeadlock.java

package com.bjpowernode.threads;
public class ThreadDeadlock {
  public static void main(String[] args) throws InterruptedException {
    Object obj1 = new Object();
    Object obj2 = new Object();
    Object obj3 = new Object();
    Thread t1 = new Thread(new SyncThread(obj1, obj2), "t1");
    Thread t2 = new Thread(new SyncThread(obj2, obj3), "t2");
    Thread t3 = new Thread(new SyncThread(obj3, obj1), "t3");
    t1.start();
    Thread.sleep(5000);
    t2.start();
    Thread.sleep(5000);
    t3.start();
  }
}
class SyncThread implements Runnable{
  private Object obj1;
  private Object obj2;
  public SyncThread(Object o1, Object o2){
    this.obj1=o1;
    this.obj2=o2;
  }
  @Override
  public void run() {
    String name = Thread.currentThread().getName();
    System.out.println(name + " acquiring lock on "+obj1);
    synchronized (obj1) {
     System.out.println(name + " acquired lock on "+obj1);
     work();
     System.out.println(name + " acquiring lock on "+obj2);
     synchronized (obj2) {
      System.out.println(name + " acquired lock on "+obj2);
      work();
    }
     System.out.println(name + " released lock on "+obj2);
    }
    System.out.println(name + " released lock on "+obj1);
    System.out.println(name + " finished execution.");
  }
  private void work() {
    try {
      Thread.sleep(30000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

在上面的程序中同步线程正完成Runnable的接口,它工作的是两个对象,这两个对象向对方寻求死锁而且都在使用同步阻塞。

在主函数中,我使用了三个为同步线程运行的线程,而且在其中每个线程中都有一个可共享的资源。

这些线程以向第一个对象获取封锁这种方式运行。但是当它试着像第二个对象获取封锁时,它就会进入等待状态,因为它已经被另一个线程封锁住了。这样,在线程引起死锁的过程中,就形成了一个依赖于资源的循环。

当我执行上面的程序时,就产生了输出,但是程序却因为死锁无法停止。

t1 acquiring lock on java.lang.Object@6d9dd520
t1 acquired lock on java.lang.Object@6d9dd520
t2 acquiring lock on java.lang.Object@22aed3a5
t2 acquired lock on java.lang.Object@22aed3a5
t3 acquiring lock on java.lang.Object@218c2661
t3 acquired lock on java.lang.Object@218c2661
t1 acquiring lock on java.lang.Object@22aed3a5
t2 acquiring lock on java.lang.Object@218c2661
t3 acquiring lock on java.lang.Object@6d9dd520

在此我们可以清楚地在输出结果中辨认出死锁局面,但是在我们实际生活所用的应用中,发现死锁并将它排除是非常难的。

分析死锁

 2012-12-27 19:08:34
Full thread dump Java HotSpot(TM) 64-Bit Server VM (23.5-b02 mixed mode):
 "Attach Listener" daemon prio=5 tid=0x00007fb0a2814000 nid=0x4007 waiting on condition [0x0000000000000000]
  java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" prio=5 tid=0x00007fb0a2801000 nid=0x1703 waiting on condition [0x0000000000000000]
  java.lang.Thread.State: RUNNABLE
"t3" prio=5 tid=0x00007fb0a204b000 nid=0x4d07 waiting for monitor entry [0x000000015d971000]
  java.lang.Thread.State: BLOCKED (on object monitor)
  at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
  - waiting to lock <0x000000013df2f658> (a java.lang.Object)
  - locked <0x000000013df2f678> (a java.lang.Object)
  at java.lang.Thread.run(Thread.java:722)
"t2" prio=5 tid=0x00007fb0a1073000 nid=0x4207 waiting for monitor entry [0x000000015d209000]
  java.lang.Thread.State: BLOCKED (on object monitor)
  at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
  - waiting to lock <0x000000013df2f678> (a java.lang.Object)
  - locked <0x000000013df2f668> (a java.lang.Object)
  at java.lang.Thread.run(Thread.java:722)
"t1" prio=5 tid=0x00007fb0a1072000 nid=0x5503 waiting for monitor entry [0x000000015d86e000]
  java.lang.Thread.State: BLOCKED (on object monitor)
  at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
  - waiting to lock <0x000000013df2f668> (a java.lang.Object)
  - locked <0x000000013df2f658> (a java.lang.Object)
  at java.lang.Thread.run(Thread.java:722)
"Service Thread" daemon prio=5 tid=0x00007fb0a1038000 nid=0x5303 runnable [0x0000000000000000]
  java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" daemon prio=5 tid=0x00007fb0a1037000 nid=0x5203 waiting on condition [0x0000000000000000]
  java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" daemon prio=5 tid=0x00007fb0a1016000 nid=0x5103 waiting on condition [0x0000000000000000]
  java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=5 tid=0x00007fb0a4003000 nid=0x5003 runnable [0x0000000000000000]
  java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=5 tid=0x00007fb0a4800000 nid=0x3f03 in Object.wait() [0x000000015d0c0000]
  java.lang.Thread.State: WAITING (on object monitor)
  at java.lang.Object.wait(Native Method)
  - waiting on <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
  - locked <0x000000013de75798> (a java.lang.ref.ReferenceQueue$Lock)
  at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
  at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)
"Reference Handler" daemon prio=5 tid=0x00007fb0a4002000 nid=0x3e03 in Object.wait() [0x000000015cfbd000]
  java.lang.Thread.State: WAITING (on object monitor)
  at java.lang.Object.wait(Native Method)
  - waiting on <0x000000013de75320> (a java.lang.ref.Reference$Lock)
  at java.lang.Object.wait(Object.java:503)
  at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
  - locked <0x000000013de75320> (a java.lang.ref.Reference$Lock)
"VM Thread" prio=5 tid=0x00007fb0a2049800 nid=0x3d03 runnable
"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007fb0a300d800 nid=0x3503 runnable
"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007fb0a2001800 nid=0x3603 runnable
"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007fb0a2003800 nid=0x3703 runnable
"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007fb0a2004000 nid=0x3803 runnable
"GC task thread#4 (ParallelGC)" prio=5 tid=0x00007fb0a2005000 nid=0x3903 runnable
"GC task thread#5 (ParallelGC)" prio=5 tid=0x00007fb0a2005800 nid=0x3a03 runnable
"GC task thread#6 (ParallelGC)" prio=5 tid=0x00007fb0a2006000 nid=0x3b03 runnable
"GC task thread#7 (ParallelGC)" prio=5 tid=0x00007fb0a2006800 nid=0x3c03 runnable
"VM Periodic Task Thread" prio=5 tid=0x00007fb0a1015000 nid=0x5403 waiting on condition
JNI global references: 114
Found one Java-level deadlock:
=============================
"t3":
 waiting to lock monitor 0x00007fb0a1074b08 (object 0x000000013df2f658, a java.lang.Object),
 which is held by "t1"
"t1":
 waiting to lock monitor 0x00007fb0a1010f08 (object 0x000000013df2f668, a java.lang.Object),
 which is held by "t2"
"t2":
 waiting to lock monitor 0x00007fb0a1012360 (object 0x000000013df2f678, a java.lang.Object),
 which is held by "t3"
Java stack information for the threads listed above:
===================================================
"t3":
  at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
  - waiting to lock <0x000000013df2f658> (a java.lang.Object)
  - locked <0x000000013df2f678> (a java.lang.Object)
  at java.lang.Thread.run(Thread.java:722)
"t1":
  at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
  - waiting to lock <0x000000013df2f668> (a java.lang.Object)
  - locked <0x000000013df2f658> (a java.lang.Object)
  at java.lang.Thread.run(Thread.java:722)
"t2":
  at com.bjpowernode.threads.SyncThread.run(ThreadDeadlock.java:41)
  - waiting to lock <0x000000013df2f678> (a java.lang.Object)
  - locked <0x000000013df2f668> (a java.lang.Object)
  at java.lang.Thread.run(Thread.java:722)
Found 1 deadlock.

这三个线程转存的输出清楚地说明了死锁环境和线程,以及包含死锁环境的资源。

为了分析死锁,我们需要关注死锁状态的线程,然后资源再等待去封锁,每一个资源都有一个独特的ID,有了这个ID我们就能发现是哪一个进程已经封锁住对象。举个例子,线程“t3”正在等待封锁0x000000013df2f658,但是它已经被线程“t1”封锁住了。

当我们分析死锁环境的时候,如果发现线程正在引起死锁,这是我们就要改变代码来避免死锁的产生。

避免死锁

有很多方针可供我们使用来避免死锁的局面。

避免嵌套封锁:这是死锁最主要的原因的,如果你已经有一个资源了就要避免封锁另一个资源。如果你运行时只有一个对象封锁,那是几乎不可能出现一个死锁局面的。例如,这里是另一个运行中没有嵌套封锁的run()方法,而且程序运行没有死锁局面,运行得很成功。

public void run() {
  String name = Thread.currentThread().getName();
  System.out.println(name + " acquiring lock on " + obj1);
  synchronized (obj1) {
    System.out.println(name + " acquired lock on " + obj1);
    work();
  }
  System.out.println(name + " released lock on " + obj1);
  System.out.println(name + " acquiring lock on " + obj2);
  synchronized (obj2) {
    System.out.println(name + " acquired lock on " + obj2);
    work();
  }
  System.out.println(name + " released lock on " + obj2);

  System.out.println(name + " finished execution.");
}

只对有请求的进行封锁:你应当只想你要运行的资源获取封锁,比如在上述程序中我在封锁的完全的对象资源。但是如果我们只对它所属领域中的一个感兴趣,那我们应当封锁住那个特殊的领域而并非完全的对象。

避免无限期的等待:如果两个线程正在等待对象结束,无限期的使用线程加入,如果你的线程必须要等待另一个线程的结束,若是等待进程的结束加入最好准备最长时间。

(0)

相关推荐

  • Java多线程 线程同步与死锁

     Java多线程 线程同步与死锁 1.线程同步 多线程引发的安全问题 一个非常经典的案例,银行取钱的问题.假如你有一张银行卡,里面有5000块钱,然后你去银行取款2000块钱.正在你取钱的时候,取款机正要从你的5000余额中减去2000的时候,你的老婆正巧也在用银行卡对应的存折取钱,由于取款机还没有把你的2000块钱扣除,银行查到存折里的余额还剩5000块钱,准备减去2000.这时,有趣的事情发生了,你和你的老婆从同一个账户共取走了4000元,但是账户最后还剩下3000元. 使用代码模拟下取款过

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

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

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

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

  • java 中死锁问题的实例详解

    java 中死锁问题的实例详解 先看代码在做解释 public class DeadLock implements Runnable{ String a; String b; boolean flag; public DeadLock(String a,String b,boolean flag){ this.a=a; this.b=b; this.flag=flag; } public void run(){ if(flag){ // while(true){ synchronized(a){

  • Java避免死锁_动力节点Java学院整理

    在有些情况下死锁是可以避免的.本文将展示三种用于避免死锁的技术: 1.加锁顺序 2.加锁时限 3.死锁检测 加锁顺序 当多个线程需要相同的一些锁,但是按照不同的顺序加锁,死锁就很容易发生. 如果能确保所有的线程都是按照相同的顺序获得锁,那么死锁就不会发生.看下面这个例子: Thread 1: lock A lock B Thread 2: wait for A lock C (when A locked) Thread 3: wait for A wait for B wait for C 如果

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

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

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

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

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

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

  • Java死锁_动力节点Java学院整理

    死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源.在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它. Java死锁范例 ThreadDeadlock.java package com.bjpowernode.threads; public class ThreadDeadlock { public static void main(String[] args) throws InterruptedException {

  • Java自定义异常_动力节点Java学院整理

    废话不多说了,直接给大家贴代码了,具体代码如下所示: /*下面做了归纳总结,欢迎批评指正*/ /*自定义异常*/ class ChushulingException extends Exception { public ChushulingException(String msg) { super(msg); } } class ChushufuException extends Exception { public ChushufuException(String msg) { super(m

  • Java枚举_动力节点Java学院整理

    enum 的全称为 enumeration, 是 JDK 5  中引入的新特性,存放在 java.lang 包中. 原始的接口定义常量 public interface IConstants { String MON = "Mon"; String TUE = "Tue"; String WED = "Wed"; String THU = "Thu"; String FRI = "Fri"; String

  • Java内部类_动力节点Java学院整理

    内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同.内部类可以是静态static的,也可用public,default,protected和private修饰.(而外部顶级类即类名和文件名相同的只能使用public和default). 注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类.对于一个名为outer的外部类和其内部定义的名为inner的内部类.编译完成后出现outer.class和outer$inner.class两类.所以内部类的成员变量/方法名可以和

  • 深入理解Java虚拟机_动力节点Java学院整理

    什么是Java虚拟机 Java程序必须在虚拟机上运行.那么虚拟机到底是什么呢?先看网上搜索到的比较靠谱的解释: 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机有自己完善的硬体架构,如处理器.堆栈.寄存器等,还具有相应的指令系统.JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行. 这种解释应该算是正确的,但是只描述了虚拟机的外部行为和功能,并没有针对内部原理

  • Java正则表达式_动力节点Java学院整理

    正则表达式是什么?用我的理解就是一个表达式.用来匹配,替换,判断字符串,之前业务就出现过判断返回值是否为邮箱.在程序开发中,难免会遇到需要匹配.查找.替换.判断字符串的情况发生,而这些情况有时又比较复杂,如果用纯编码方式解决,往往会浪费程序员的时间及精力.因此,学习及使用正则表达式,便成了解决这一矛盾的主要手段. 正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待匹配的一个或多个字符串

  • Java适配器模式_动力节点Java学院整理

    一.概念 适配器模式将一个接口转换成客户希望的另外一个接口.它使得原来由于接口不兼容而不能在一起工作的那些类可以一起工作. 二.UML 三.更加生动的例子 四.实例分析 去年买了一个本本,另外给本本配了罗技G1光电套.坑爹的是,光电套的鼠标是USB接口,键盘是PS2接口,可我的本本却没有PS2接口啊.于是跑到市场,淘了一个转接器. 于是乎,我抽象了这么几个类. 1.PS2Port(PS2接口). 2.USBPort(USB接口). 3.PS2ToUSB(对象适配器),将PS2接口装换成USB接口

  • EL调用Java方法_动力节点Java学院整理

    简单来说,我们在一个类中的某个方法,可以使用EL进行调用,这个能被EL表达式调用的方法称之为EL函数,但是这种方式必须满足以下两点要求: ① 在EL表达式中调用的只能是Java类的静态方法 ② 这个Java类的静态方法需要在我们另外在自定义的TLD文件中描述 满足以上两点才能被EL表达式调用. 语法:  ${prefix:方法名(参数列表) } 例1:使用EL函数来实现HTML语言的过滤转义 在web工程中自定义一个HTMLFilter类,同时定义一个静态的filter方法: package c

  • Java异常继承结构解析_动力节点Java学院整理

    Java异常类层次结构图: 异常的英文单词是exception,字面翻译就是"意外.例外"的意思,也就是非正常情况.事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误.比如使用空的引用.数组下标越界.内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图.错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的.假若程序在运行期间出现了错误

随机推荐