Java并发编程实战重入锁(子类同步方法调用父类的同步方法)

看如下代码
public class Widget {

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

    public synchronized void doSomething () {
        System.out.println(this.toString()+ ": calling doSomething");
        super.doSomething();
    }

    public static void main (String[] args) throws InterruptedException {
        for (int i = 0; i < 2; i++) {
            Thread thread = new Thread(() -> {
                Widget widget = new LoggingWidget();
                widget.doSomething();
            });
            thread.start();
        }
    }
}
  • 书上说如果内置锁不是可重入的,那么这段代码将发生死锁.我们先来理解一下什么是重入锁,当某个线程请求一个由某个线程持有的锁时,发出的请求就会阻塞.然而,由于内置锁时可重入的,因此如果某个线程试图获得一个已经由它自己持有的锁,那么这个请求就会成功.
  • 理解了重入锁,我们来看下几种synchronized的锁对象,同步方法的锁对象:this,静态的锁对象:当前类的class对象,同步代码块的锁对象:任意对象.
  • 接下来我们来分析下代码的执行,假设线程A初始化了LoggingWidget 对象,接着去执行doSomething 方法,由于doSomething 方法是非静态同步方法,所以锁对象就是this对象本身,获取对象锁,然后打印this.toString,接着调用super.doSomething 方法,由于是同步方法所以也需要获取锁对象,由于线程A已经获取了对象锁,所以super.doSomething方法如果和doSomething 方法是同一个锁对象就可以执行,如果不是就获取不到锁,就会产生死锁,永远获取不到.笔者在这里不理解的问题是super.doSomething()是谁调用的,如果父类,那么这个同步方法的锁对象就是父类本身,那么线程A不可能同时获取两个锁对象.如果是子类调用的线程A已经获取了锁对象,所以就可以执行.
  • 我们来看下运行结果:
    com.concyrrency.test.lock.LoggingWidget@2cb7a5c1: calling doSomething
    com.concyrrency.test.lock.LoggingWidget@2cb7a5c1---------------------
    com.concyrrency.test.lock.LoggingWidget@77df833b: calling doSomething
    com.concyrrency.test.lock.LoggingWidget@77df833b---------------------
  • 可以看到两个线程的分别打印的this.toString都是一样的,说明super.doSomething()的调用者是子类,而且内置锁是可以重入的.
  • 理解super关键字,在Java类中使用supper来引用分类的成分,用this来引用当前的对象,如果一个类从另一个类继承,new这个子类实例对象的时候,这个子类对象会含有一个父类对象,怎么去引用父类的对象了,使用super来停用,this指的是当前对象的引用,super是当前对象里面父类的引用.


网页名称:Java并发编程实战重入锁(子类同步方法调用父类的同步方法)
本文URL:http://pwwzsj.com/article/pecdpp.html