【转】Java并发基础实践--死锁

本文是 Java并发基础实践 系列中的一篇,介绍了最简单的死锁场景,并使用jstack产生的thread dump来查找死锁。(2013.12.29最后更新)

1. 死锁
为了能够维护线程的安全性,Java提供的锁机制,但不恰当地使用锁则可能产生死锁。死锁是并发编程中一个无法绕开的问题。只要在一个任务中使用了一个以上的锁,那么就存在死锁的风险。
死锁产生的直接原因非常简单,即两个线程在相互等待对方所执有的锁。

2. 锁顺序死锁
在死锁场景中,最典型的就是锁顺序死锁,代码清单1就是一个很常见的示例。
清单1
public   class  DeadLock {

    
private  Object leftLock  =   new  Object();
    
private  Object rightLock  =   new  Object();

    
public   void  leftRight() {
        
synchronized  (leftLock) {
            
try  {
                TimeUnit.SECONDS.sleep(
3 );
            } 
catch  (InterruptedException e) {
                e.printStackTrace();
            }

            
synchronized  (rightLock) {
                System.out.println(
" leftRight " );
            }
        }
    }

    
public   void  rightLeft() {
        
synchronized  (rightLock) {
            
try  {
                TimeUnit.SECONDS.sleep(
3 );
            } 
catch  (InterruptedException e) {
                e.printStackTrace();
            }

            
synchronized  (leftLock) {
                System.out.println(
" leftRight " );
            }
        }
    }

    
public   static   void  main(String[] args) {
        
final  DeadLock deadLock  =   new  DeadLock();

        Thread t1 
=   new  Thread( new  Runnable() {

            @Override
            
public   void  run() {
                deadLock.leftRight();
            }
        });

        Thread t2 
=   new  Thread( new  Runnable() {

            @Override
            
public   void  run() {
                deadLock.rightLeft();
            }
        });

        t1.start();
        t2.start();
    }
}

3. Thread Dump
JDK提供了一组命令行工具,其中就包括jstack。通过jstack可以获取当前正运行的Java进程的java stack和native stack信息。如果Java进程崩溃了,也可以通过它来获取core file中的java stack和native stack信息,以方便我们定位问题。
为了能够使用jstack去输出目标Java进程的thread dump,首先必须要弄清楚在执行清单1的程序时,该程序的进程号。JDK提供的另一个命令行工具jps可以获取系统中所有Java进程的相关信息。
在命令行窗口中执行命令 jps ,即可以得到清单2所示的结果
清单2
C:\Documents and Settings\Administrator>jps
2848
4552  DeadLock
5256  Jps
其中 4552 就是在笔者机器上执行程序DeadLock时所生成Java进程的进程号。
然后再执行命令 jstack 4552 ,在笔者的机器上就会得到清单3所示的结果
清单3
C:\Documents and Settings\Administrator>jstack 
4552
2013 - 12 - 29   18 : 45 : 41
Full thread dump Java HotSpot(TM) Client VM (
23.25 -b01 mixed mode ,  sharing):

" DestroyJavaVM "  prio = 6  tid = 0x00878800 nid = 0xd00 waiting on condition  [ 0x00000000 ]
   java.lang.Thread.State: RUNNABLE

" Thread-1 "  prio = 6  tid = 0x02b56c00 nid = 0x14ec waiting for monitor entry  [ 0x02fdf000 ]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at concurrency.deadlock.DeadLock.rightLeft(DeadLock.java:
33 )
        - waiting to lock <0x22be6598> (a java.lang.Object)
        - locked <0x22be65a0> (a java.lang.Object)
        at concurrency.deadlock.DeadLock$
2 .run(DeadLock.java: 53 )
        at java.lang.Thread.run(Thread.java:
724 )

" Thread-0 "  prio = 6  tid = 0x02b55c00 nid = 0x354 waiting for monitor entry  [ 0x02f8f000 ]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at concurrency.deadlock.DeadLock.leftRight(DeadLock.java:
19 )
        - waiting to lock <0x22be65a0> (a java.lang.Object)
        - locked <0x22be6598> (a java.lang.Object)
        at concurrency.deadlock.DeadLock$
1 .run(DeadLock.java: 45 )
        at java.lang.Thread.run(Thread.java:
724 )

" Service Thread "  daemon prio = 6  tid = 0x02b34800 nid = 0x133c runnable  [ 0x00000000 ]
   java.lang.Thread.State: RUNNABLE

" C1 CompilerThread0 "  daemon prio = 10  tid = 0x02b13800 nid = 0x10fc waiting on condition  [ 0x00000000 ]
   java.lang.Thread.State: RUNNABLE

" Attach Listener "  daemon prio = 10  tid = 0x02b11c00 nid = 0x1424 waiting on condition  [ 0x00000000 ]
   java.lang.Thread.State: RUNNABLE

" Signal Dispatcher "  daemon prio = 10  tid = 0x02b10800 nid = 0x1100 runnable  [ 0x00000000 ]
   java.lang.Thread.State: RUNNABLE

" Finalizer "  daemon prio = 8  tid = 0x02af4c00 nid = 0x1238 in Object.wait()  [ 0x02daf000 ]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x22b60fb8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:
135 )
        - locked <0x22b60fb8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:
151 )
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:
189 )

" Reference Handler "  daemon prio = 10  tid = 0x02af0000 nid = 0x12e8 in Object.wait()  [ 0x02d5f000 ]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x22b60da0> (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 <0x22b60da0> (a java.lang.ref.Reference$Lock)

" VM Thread "  prio = 10  tid = 0x02aee400 nid = 0x129c runnable

" VM Periodic Task Thread "  prio = 10  tid = 0x02b48000 nid = 0x89c waiting on condition

JNI global references: 
117


Found one Java-level deadlock:
=============================
" Thread-1 " :
  waiting to lock monitor 0x02af4a3c (object 0x22be6598
,  a java.lang.Object) ,
  which is held by 
" Thread-0 "
" Thread-0 " :
  waiting to lock monitor 0x02af310c (object 0x22be65a0
,  a java.lang.Object) ,
  which is held by 
" Thread-1 "

Java stack information for the threads listed above:
===================================================
" Thread-1 " :
        at concurrency.deadlock.DeadLock.rightLeft(DeadLock.java:
33 )
        - waiting to lock <0x22be6598> (a java.lang.Object)
        - locked <0x22be65a0> (a java.lang.Object)
        at concurrency.deadlock.DeadLock$
2 .run(DeadLock.java: 53 )
        at java.lang.Thread.run(Thread.java:
724 )
" Thread-0 " :
        at concurrency.deadlock.DeadLock.leftRight(DeadLock.java:
19 )
        - waiting to lock <0x22be65a0> (a java.lang.Object)
        - locked <0x22be6598> (a java.lang.Object)
        at concurrency.deadlock.DeadLock$
1 .run(DeadLock.java: 45 )
        at java.lang.Thread.run(Thread.java:
724 )

Found 
1  deadlock.
在上述输出中,我们可以很明确地看到一个死锁
" Thread-1 " :
  waiting to lock monitor 0x02af4a3c (object 0x22be6598
,  a java.lang.Object) ,
  which is held by 
" Thread-0 "
" Thread-0 " :
  waiting to lock monitor 0x02af310c (object 0x22be65a0
,  a java.lang.Object) ,
  which is held by 
" Thread-1 "
并且它还标明了程序是在哪个地方时发现了上述死锁
" Thread-1 " :
        at concurrency.deadlock.DeadLock.rightLeft(DeadLock.java:
33 )
        - waiting to lock <0x22be6598> (a java.lang.Object)
        - locked <0x22be65a0> (a java.lang.Object)
        at concurrency.deadlock.DeadLock$
2 .run(DeadLock.java: 53 )
        at java.lang.Thread.run(Thread.java:
724 )
" Thread-0 " :
        at concurrency.deadlock.DeadLock.leftRight(DeadLock.java:
19 )
        - waiting to lock <0x22be65a0> (a java.lang.Object)
        - locked <0x22be6598> (a java.lang.Object)
        at concurrency.deadlock.DeadLock$
1 .run(DeadLock.java: 45 )
        at java.lang.Thread.run(Thread.java:
724 )

4. 小结
死锁产生的直接原因非常简单,即两个线程在相互等待对方所执有的锁。锁顺序死锁是其中最经典的场景,此外还有动态的锁顺序死锁。虽然表现形式有所不同,但本质上都是两个线程在以不同的顺序来获取相同锁时,发生了死锁问题。

使用thread dump可以帮助我们分析死锁产生的原因。除了直接使用jstack命令来获取thread dump输出以外,JDK还提供了jvisualvm工具,它能以可视化的方式展示Java程序的进程号并导出thread dump。


原文:http://www.blogjava.net/jiangshachina/archive/2013/12/29/408180.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值