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


Java死锁范例


ThreadDeadlock.java


package com.journaldev.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


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


分析死锁


为了分析一个死锁,我们需要关注下应用中的Java线程转存,在上一节中我已经解释了如何使用VisualVM收集资料或者jstack应用程序产生线程转存。


以下就是上述程序的线程转存。


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.journaldev.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.journaldev.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.journaldev.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.journaldev.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.journaldev.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.journaldev.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.");

}


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


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


更多相关文章

  1. Java线程之概念与原理
  2. Java 线程面试题 Top 50
  3. Java后端开发三年多线程你都懂,问你异步编程你说你没听过???
  4. 调用没有当前上下文的OpenGL ES API(每个线程记录一次)
  5. 具有线程/回复的私人消息系统
  6. PHP多线程编程
  7. 使用多个线程更新DB时如何避免“锁超时”?
  8. 再论mysql线程出现Waiting for table flush被hang住问题
  9. mysql批量结束线程

随机推荐

  1. android 修改statusbar(二)
  2. Android中获得上下文的静态方法
  3. Android中利用DisplayMetrics取得手机屏
  4. android 幾個快速編譯images指令
  5. Android ListView 长按列表弹出菜单
  6. Android实现富文本编辑
  7. 判断android网络连接
  8. [Android Studio]使用AS开发Android
  9. android studio 模拟器出现中文乱码解决
  10. Android遇到 android studio "Internal H