天天看点

嵌套管程锁死

嵌套管程锁死类似于死锁, 下面是一个嵌套管程锁死的场景:

你可以能会说,这是个空想的场景,好吧,让我们来看看下面这个比较挫的lock实现:

可以看到,lock()方法首先在”this”上同步,然后在monitorobject上同步。如果islocked等于false,因为线程不

会继续调用monitorobject.wait(),那么一切都没有问题

。但是如果islocked等于true,调用lock()方法的线程会在monitorobject.wait()上阻塞。

这里的问题在于,调用monitorobject.wait()方法只释放了monitorobject上的管程对象,而与”this“关联的管程对象并没有释放。换句话说,这个刚被阻塞的线程仍然持有”this”上的锁。

(校对注:如果一个线程持有这种lock的时候另一个线程执行了lock操作)当一个已经持有这种lock的线程想调用

unlock(),就会在unlock()方法进入synchronized(this)块时阻塞。这会一直阻塞到在lock()方法中等待的线程离开

synchronized(this)块。但是,在unlock中islocked变为false,monitorobject.notify()被执行

之后,lock()中等待的线程才会离开synchronized(this)块。

简而言之,在lock方法中等待的线程需要其它线程成功调用unlock方法来退出lock方法,但是,在lock()方法离开外层同步块之前,没有线程能成功执行unlock()。

结果就是,任何调用lock方法或unlock方法的线程都会一直阻塞。这就是嵌套管程锁死。

你可能会说,这么挫的实现方式我怎么可能会做呢?你或许不会在里层的管程对象上调用wait或notify方法,但完全有可能会在外层的this上调。

有很多类似上面例子的情况。例如,如果你准备实现一个公平锁。你可能希望每个线程在它们各自的queueobject上调用wait(),这样就可以每次唤醒一个线程。

下面是一个比较挫的公平锁实现方式:

乍看之下,嗯,很好,但是请注意lock方法是怎么调用queueobject.wait()的,在方法内部有两个synchronized块,一个锁定this,一个嵌在上一个synchronized块内部,它锁定的是局部变量queueobject。

当一个线程调用queueobject.wait()方法的时候,它仅仅释放的是在queueobject对象实例的锁,并没有释放”this”上面的锁。

现在我们还有一个地方需要特别注意,

unlock方法被声明成了synchronized,这就相当于一个synchronized(this)块。这就意味着,如果一个线程在lock()

中等待,该线程将持有与this关联的管程对象。所有调用unlock()的线程将会一直保持阻塞,等待着前面那个已经获得this锁的线程释放this

锁,但这永远也发生不了,因为只有某个线程成功地给lock()中等待的线程发送了信号,this上的锁才会释放,但只有执行unlock()方法才会发

送这个信号。

嵌套管程锁死与死锁很像:都是线程最后被一直阻塞着互相等待。

但是发生嵌套管程锁死时锁获取的顺序是一致的。线程1获得a和b,然后释放b,等待线程2的信号。线程2需要同时获得a和b,才能向线程1发送信号。所以,一个线程在等待唤醒,另一个线程在等待想要的锁被释放。

不同点归纳如下:

死锁中,二个线程都在等待对方释放锁。

嵌套管程锁死中,线程1持有锁a,同时等待从线程2发来的信号,线程2需要锁a来发信号给线程1。

继续阅读