Java可重入锁如何避免死锁

本文由https://bbs.csdn.net/topics/390939500https://zhidao.baidu.com/question/1946051090515119908.html启发而来。

看到一个问题,Java的可重入锁为什么可以防止死锁呢?网上看了看资料,虽然有答案说出了正确答案,但是分析的不够详细,对初学者不够友好。这里我再做一个更清晰的分析。

首先是示例代码:

public class Widget {
    public synchronized void doSomething(){
        // do something
    }
}
public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        super.doSomething();
    }
}

这是《java并发编程实例》一书中的例子,并且书中说:“如果synchronized 不是可重入锁,那么LoggingWidget 的super.dosomething();无法获得Widget对象的锁,因为会死锁。”

乍一看好像不是这么回事,就算synchronized 不是可重入锁,可是synchronized 关键字一个在父类Widget 的方法上,另一个在子类LoggingWidget 的方法上,怎么会有死锁产生呢。

这里其实牵涉到了Java的重写。我们看子类LoggingWidget 的doSomething方法,重写了父类Widget 的doSomething方法,但是子类对象如果要调用父类的doSomething方法,那么就需要用到super关键字了。因为实例方法的调用是Java虚拟机在运行时动态绑定的,子类LoggingWidget 的对象调用doSomething方法,一定是绑定到子类自身的doSomething方法,必须用super关键字告诉虚拟机,这里要调用的是父类的doSomething方法。

实际上,如果我们分析运行时的LoggingWidget 类,那我们看到的应该是这样子的(这里只是为了分析,真实情况肯定和下面的例子不同):

public class LoggingWidget extends Widget {
    public synchronized void Widget.doSomething() {
        // do something
    }   // 父类的doSomething方法

    public synchronized void doSomething() {
        super.doSomething();
    }
}

子类对象,其实是持有父类Widget 的doSomething方法的,只需要使用super关键字告诉虚拟机要运行的是父类的doSomething方法,虚拟机会去调用子类对象中的父类Widget 的doSomething方法的。所以,super关键字并没有新建一个父类的对象,比如说widget,然后再去调用widget.doSomething方法,实际上调用父类doSomething方法的还是我们的子类对象。

那么这样就很好理解了,如果一个线程有子类对象的引用loggingWidget,然后调用loggingWidget.doSomething方法的时候,会请求子类对象loggingWidget 的对象锁;又因为loggingWidget 的doSomething方法中调用的父类的doSomething方法,实际上还是要请求子类对象loggingWidget 的对象锁,那么如果synchronized 关键字不是个可重入锁的话,就会在子类对象持有的父类doSomething方法上产生死锁了。正因为synchronized 关键字的可重入锁,当前线程因为已经持有了子类对象loggingWidget 的对象锁,后面再遇到请求loggingWidget 的对象锁就可以畅通无阻地执行同步方法了。

更进一步,将上面的示例代码改写一下,那么就算synchronized 不是可重入锁,也不会产生死锁的问题了。代码如下:

public class Widget {
    public synchronized void doSomething(){
        // do something
    }
}
public class LoggingWidget extends Widget {
    public synchronized void doSomething() {
        Widget widget = new Widget();
        widget.doSomething();
    }
}

在子类的doSomething方法中,直接新建了一个父类的对象widget,然后用这个父类对象来调用父类的doSomething方法,实际上请求的是这个父类对象widget的对象锁,就不涉及到可重入锁的问题了。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页