最近在阅读《java并发编程实践》一书。在2.3.2章节里有描述synchronized锁有可重进入的特性。书中描述如下:
当一个线程请求其它的线程已经占有的锁时,请求线程将被阻塞。然而内部锁是可重进入的,因此线程在试图获得它自己占用的锁是,请求会成功。重进入意味着请求是基于“每一个线程”,而不是基于“每一次调用”(互斥锁是基于每次调用的)。重进入的实现是通过为每一个锁关联一个请求技术器和一个占有他的线程。当计数为0时,认为锁是未被占用的。线程请求一个未被占有的锁时候,JVM将记录锁的占有者,并且将请求计数设置为1。如果同一个线程再次请求这个锁,计数将递增;每次占用线程退出语句块时,计数器值将递减,直到计数器达到0时候,锁被释放。
public class Widget { public synchronized void doSomething() { ... } } public class LoggingWidget extends Widget { public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething();//若内置锁是不可重入的,则发生死锁 } }
1.线程先获取子类LoggingWidget的锁对象;
2.线程进入子类LoggingWidget的doSomething方法中;
3.在访问父类方法doSomething时,也要获取调用doSometing方法的对象(即当前的LoggingWidget对象)。
就在这一步,如果没有可重入的锁,子类中可能就会产生死锁。因为Widget和LoggingWidget中的dosomething方法都是synchronized 类型的,都会在处理前试图获得LoggingWidget的锁。倘若内部锁不是可重入的,super.doSomething的调用者就永远无法获得Widget的锁。因为锁已经被占用,导致线程永久的延迟,等待着一个永远无法获得的锁。
另追踪线程Dump显示信息如下:
LoggingWidget 的对象调用doSomething方法时,锁对象为LoggingWidget对象 super.doSomething()调用是锁对象是LoggingWidget对象 运行程序,查看thread dump发现:调用super.doSomething()时锁对象依然是LoggingWidget对象。
"线程#1" prio=6 tid=0x0bd60400 nid=0x16f8 waiting on condition [0x0bf8f000..0x0bf8fd68]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Widget.doSomething(Widget.java:4)
- locked <0x03fbc150> (a LoggingWidget)
at LoggingWidget.doSomething(LoggingWidget.java:5)
- locked <0x03fbc150> (a LoggingWidget)
at LoggingWidget$1.run(LoggingWidget.java:15)
Locked ownable synchronizers:
- None
再举例证明内部锁synchronized的可重进入特性,代码如下:
<span style="color:#000000;">package com.lkg.ec.thread; public class Someclass { private static final Object LCK_OBJ = new Object(); public void method1() { System.out.println(Thread.currentThread().getName() + " method1"); synchronized (LCK_OBJ) { System.out.println(Thread.currentThread().getName() + " ready in method3"); method3(); } } public void method3() { synchronized (LCK_OBJ) { System.out.println(Thread.currentThread().getName() + " method3"); } } public static void main(String[] args) { Someclass sc = new Someclass(); sc.method1(); } }</span>
参考资料:
1.http://topmanopensource.iteye.com/blog/1736739
2.http://stackoverflow.com/questions/5787957/reentrant-synchronization-behavior-with-synchronized-statements