Java并发编程实战-内置锁不是重入的,那么这段代码将发生死锁-以及书籍勘误

引出:

在《Java并发编程实战》的2.3.2重入章节中提到了“由于Widget和LoggingWidget中的doSomething方法都是synchronized方法,因此每个doSomething方法在执行前都会获取Widget上的锁。 ”那么问题就来了,为何每个doSomething方法都会需要获取Widget的锁呢?

代码:

public class Widget {
public synchronized void doSomething(){
    //...
}
}

class LoggingWidget extends Widget{
public synchronized void doSomething(){
    System.out.println(toString()+":calling doSomething");
    super.doSomething();
}
}

重入问题的解决

  1. 首先这个问题涉及了Java中的继承以及Super关键字的语法,必须把握住这一中心要点,才能理解这个问题。我们可以这样认为,子类继承父类,并调用子类构造器新建子类对象时,会在内存空间里开辟子类对象自身的实例域以及从父类继承过来的实例域,但他们都属于子类对象所管理的域,并不会创建额外的空间来存储父类对象的实例域。其次,从父类继承过来的实例域是通过调用子类构造器过程中的调用(编译器默认自动进行的,不用显示写出)的父类构造器所创建的。
  2. 遇到new关键字,JVM分步的实现方式:
    1)先分配空间(父类中的实例变量和子类中的实例变量,注意先后顺序)
    2)初始化默认值
    3) 调用当前类的 < init >(注意<init的结构)
  3. 在《Java并发编程实战》中的同步代码块(方法)锁的定义为:方法调用所在的对象,这就意味着——是否要考虑重入要依据是否是同一锁也就是同一个对象的访问。然而,由1和2可以推出一点,创建子类对象的时候并未创建父类对象,也就是说对于同名的doSomething()方法,子类对象中有俩,且都是用synchronized修饰,只不过一个是子类对于父类的继承以及重写,用this来修饰;另一个是继承来自于父类(未重写),用super来区分。
  4. 1到3点我们可以推出应当把子类的doSomething()方法和父类的super.doSomething()方法看作同一对象的不同方法(前者使用this关键字且默认省去,后者则用了super关键字且不能省略),且都是被synchronized修饰。由于锁即对象,所以重入又可以看作是关于同一对象中访问不同synchronized修饰的方法问题。

总结

调用子类LoggingWidget类构造方法的时候,只会构造子类对象,其中包含来自父类的域。由于父类和子类中都有syunchronized修饰的方法,所以进行分步骤(时间上)调用这俩方法,即一个进程调用自己所持有的锁,这个时候就引起了内置锁的重入机制。
至于为何每个doSomething方法都会需要获取子类对象LoggingWidget的锁,它的解释是由于至始至终只创建了一个对象,锁即对象,相同对象对应相同锁,相同锁的同一进程重复访问需要重入机制。

参考网址

https://www.zhihu.com/question/51920553/answer/128761716
https://www.zhihu.com/question/28113814
https://stackoverflow.com/questions/27900332/reentrant-lock-java-concurrency-in-practice
https://ask.csdn.net/questions/768807

补充

《Java并发编程实战》重入小节中对于锁的归属问题并没有写正确,实际上调用子类doSometing方法时,请求的锁都是子类对象LoggingWidget的锁,并非是父类对象的锁,一个有力的辩驳理由是:父类对象从始至终没有被创建,JVM中只有子类对象。
对于这个问题,我们可以以两个方面理解。一方面,书本上写到:“每次获取的是子类Widget对象的锁”这句话一方面可以认为其说得不够明确,是错误的。另一方面,由于Java鼎鼎有名的多态性质,那么在许多代码中子类对象和父类对象的确没有必要去细分,所以也可以认为此处说法没有错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值