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

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

版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

引出:

在《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鼎鼎有名的多态性质,那么在许多代码中子类对象和父类对象的确没有必要去细分,所以也可以认为此处说法没有错误。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java并发-推荐使用不可变对象的原因分析

    在Java语法中,String即是不可变对象,一旦创建,假设你若想修改String对象值,只能重新创建String对象。 实现方式如下:1.将内部char类型...

    Fisherman渔夫
  • Java-匿名子类(匿名内部类)

    版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons)

    Fisherman渔夫
  • 北邮通信原理知识点笔记小结-上半部分

    数字通信比模拟通信有着更强的抗干扰能力,可以消除噪声积累,便于集成化、加密性能好,但是代价是什么呢?

    Fisherman渔夫
  • 「JAVA」面向对象三大特征:封装、继承、多态,以高内聚、低耦合为目标

    面向对象的最终目的是要构建强健、安全、高效的项目,也就是要实现项目的高内聚和低耦合:

    老夫编程说
  • 什么是面向对象

    面向对象的特征有3个,封装、继承、多态。至于抽象的话,个人认为,应该是前面3大特征中都有抽象的思想,毕竟面向对象本身就是一种抽象。 比如 子类 extends ...

    haoming1100
  • 多态易错题

    ①②③比较好理解,一般不会出错。④⑤就有点糊涂了,为什么输出的不是”B and B”呢?!!先来回顾一下多态性。

    栋先生
  • 以太坊交易(tx) 分析

    更多请参考: Github: [https://github.com/xianfeng92/ethereum-code-analysis](https://gi...

    欧文kyri
  • JavaScript导出excel文件,并修改文件样式

    因为最近需要实现前端导出 excel 文件,并且对导出文件的样式进行一些修改,比如颜色、字体、合并单元格等,所以我找到了 xlsx-style 这个项目,它可以...

    FEWY
  • 项目里文件名永远不要用中文!永远不要!

    最近碰到了一个问题,项目中很多文件都是接手过来的中文命名的一些素材,结果在部署的时候文件名全都乱码了,导致项目无法正常运行。

    崔庆才
  • 在vc vs2017 ide中加入QT UI.

    首先vs要安装QT 插件。QT VS Tools 然后右键工程目录里面需要建立UI文件的文件夹,添加-》新建项,选择QT,出现QT UI模板,选择需要的。

    xiny120

扫码关注云+社区

领取腾讯云代金券