在《java并发编程实践》的第二章,介绍到了“可重入锁”的概念和作用,并且指出java的内置锁synchronized就是一种可重入锁。其中提到了Widget和LogginWidget,源码如下
public class Widget {
public synchronized void doSomething() {
// do somethig here...
}
}
public class LoggingWidget extends Widget {
public synchronized void doSomething() {
System.out.println(toString() + ": calling doSomething");
super.doSomething();
}
}
书中的描述如下:
子类覆写了父类的synchonized方法,然后调用父类中的方法,此时如果没有重入的锁,那么这段代码将产生死锁。由于Widget 和LoggingWidget 中的doSomething方法都是synchonized方法,因此每个doSomething方法执行前都会获取Widget 上的锁。然而如果内置锁不是可重入的,那么在调用super.doSomething()时将无法获取Widget 上的锁,因为这个锁已经被持有,从而线程将永远停顿下去,等待一个永远也无法获得的锁。

看到红色部分的字体,想必大家会觉得疑惑。 线程进入LoggingWidget.doSomething()时获取的锁不应该是LoggingWidget对象锁吗 怎会是Widget上的锁 super.doSomething()获取的究竟是哪个对象锁呢?对于喜欢追究细节的我来说,此时有了种种疑问,尤其是写完第一篇博客: java并发编程实践学习(一)java的类锁和对象锁。按照字面的意思,貌似是说:执行子类对象的同步方法时候,也会获取父对象的锁,如果不是可重入锁的话,再次调用super.doSomething()想要第二次获取Widget对象的锁,就不会成功。
< http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHA+1eLA777NsvrJ+sHLMbj2zsrM4qO6yrLDtMrH19PA4LbUz/OjrMqyw7TKx7i4wOC21M/zo7/Kx7K7yse0tL2o19PA4LbUz/Msv8+2qLvhtLS9qNK7uPa4uMDgtcS21M/zPzwvcD4KPHA+ytfPyLS0vajSu7j219PA4LbUz/O1xMqxuvLKx7K7u+G0tL2o0ru49ri4wOC21M/ztcSjrLi4wOC21M/zyse4+bG+sru05tTatcSho87Sw8e/ydLUyrnTw7e01qS3qKOsvNnI58u1tLS9qNfTwOC21M/ztcTNrMqxu+G0tL2o0ru49ri4wOC21M/zo6zEx8jnufu4uMDgysez6c/zwOCjrLK7xNzKtcD9u6/E2KO/ztLDx9aqtcDKudPDQSBhID0gbmV3IEEoKdXi1ta3vcq9tLS9qLbUz/O1xMqxuvKjrEpWTbvh1Nq688youPjO0sPHt9bF5MTatOa/1bzko6zIu7rztffTw7m51Oy6r8r91rTQ0LP1yry7r7LZ1/ejrNfuuvO3tbvYxNq05r/VvOS1xNL908Oho7y0ubnU7Lqvyv3Wu8rHvfjQ0LP1yry7r6OssqKyu7i61PC31sXkxNq05r/VvOSjqLS0vai21M/zo6mho8v50tTE2MbkyrW0tL2o19PA4LbUz/O1xMqxuvKjrEpWTbvhzqrX08DgttTP87fWxeTE2rTmv9W85KOssqK199PDuLjA4LXEubnU7Lqvyv2ho87Sw8e/ydLU1eLR+cDtveKjurS0vajBy9K7uPbX08DgttTP87XEyrG68qOs1NrX08DgttTP88TatObW0KOs09DBvbfdyv2+3aOs0ru33bzMs9DX1Li4wOCjrNK7t93AtNfU19PA4KOstavKx8v7w8fK9NPazazSu7j2ttTP86Oo19PA4LbUz/OjqaOs1ruyu7n9ysdqYXZh0++3qMzhuanBy3RoaXO6zXN1cGVyudi8/NfWwLTIw87Sw8fE3Lm7sLTV1dDo0qq3w87K1eIyt93K/b7dtvjS0aGj1eLR+b7NsvrJ+sHL19PA4LrNuLjA4LXEuMXE7qOstavKtbzKyc/Wu9PQ19PA4LbUz/OjrMO709C4uMDgttTP86GjPC9wPgo8cD48YnI+CjwvcD4KPHA+tb3V4sDvo6zO0sPH0rK+zcTcubvA7b3iobZqYXZhsqK3orHgs8zKtbz5obfW0LXEu7DBy6GjPC9wPgo8cD48cHJlIGNsYXNzPQ=="brush:java;">LoggingWidget widget = new LoggingWidget(); widget.doSomengthing();
由于doSomething()是synchronized方法,所以执行的时候,会先获取widget对象的锁;当执行到super.doSomething()的时候,由于父类中的方法也是synchronized方法,所以也必须先获取对象的锁。因为不存在所谓的父对象,或者说父对象就是子对象,所以需要获取的也是widget对象的锁。这样如果不是可重入的锁的话,就会产生死锁。
接下来我们就通过例子来证明:父类对象就是子类对象,即父类的synchronized方法和子类的synchronized方法属于同一个对象。
package net.aty.lock.extend;
public class BaseClass
{
public synchronized void doSomeThing()
{
System.out.println("parent class:begin.....doSomeThing");
try
{
Thread.sleep(200);
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("parent class:end.....doSomeThing");
}
}
package net.aty.lock.extend;
public class ChildClass extends BaseClass
{
public synchronized void childMethod()
{
System.out.println("---child class:begin.....childMethod");
try
{
Thread.sleep(200);
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("---child class:end.....childMethod");
}
}
package net.aty.lock.extend.thread;
import net.aty.lock.extend.BaseClass;
public class DemoThread1 extends Thread
{
private BaseClass base = null;
public DemoThread1(BaseClass base)
{
this.base = base;
}
@Override
public void run()
{
base.doSomeThing();
}
}
package net.aty.lock.extend.thread;
import net.aty.lock.extend.ChildClass;
public class DemoThread2 extends Thread
{
private ChildClass child = null;
public DemoThread2(ChildClass child)
{
this.child = child;
}
@Override
public void run()
{
child.childMethod();
}
}
我的测试思路是:让一个线程去访问父类中的synchronized方法,然后再让另一个线程访问子类的synchronized方法。由于2个线程竞争的是同一个对象的锁,那么线程1不执行完毕,线程2是不会开始执行的。
package net.aty.lock.extend;
public class ChildClass extends BaseClass
{
public synchronized void childMethod()
{
System.out.println("---child class:begin.....childMethod");
try
{
Thread.sleep(200);
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("---child class:end.....childMethod");
}
}
执行结果是:
parent class:begin.....doSomeThing
...main running...
...main running...
parent class:end.....doSomeThing
---child class:begin.....childMethod
---child class:end.....childMethod
很显然程序符合了我们的预期,的确不存在父对象,访问父类的同步方法,跟访问子类的同步方法没有什么实质性的差别,都是要获取子类对象的锁。