在同一个对象上进行两次同步?

58
我在想,在Java中,如果我在同一个对象上进行两次同步,是否会出现任何奇怪的行为?
情景如下:
pulbic class SillyClassName {

    object moo;
    ...
    public void method1(){
        synchronized(moo)
        {
            ....
            method2();
            ....
        }
    }

    public void method2(){
        synchronized(moo)
        {
            doStuff();
        }
    }
}

这两种方法都使用同一个对象并对其进行同步。当第一个方法调用第二个方法时,第二个方法会停止吗,因为它被锁定了吗?

我认为不会,因为它是同一线程,但我不确定是否会出现其他奇怪的结果。


想知道如果从方法1不调用方法2会发生什么,以及如果例如对象1和2同时调用方法1和2,会发生什么。由于这两种方法都锁定在同一个对象moo上,因此这两种方法中只有一种会被执行一次? - user3366706
6个回答

86

可重入锁

可重入锁是指在同步块中使用的锁,如果线程已经持有了该锁,则可以重新获取它而不会出现问题。因此,您的代码将按照预期工作。

请参见Java教程页面底部的内置锁和同步

引用自2015-01:

可重入同步

回想一下,一个线程不能获取另一个线程拥有的锁。但是,一个线程可以获取它已经拥有的锁。允许一个线程多次获取同一把锁,这使得可重入同步成为可能。这种情况描述的是同步代码直接或间接地调用包含同步代码的方法,并且两组代码都使用同一把锁。如果没有可重入同步,同步代码将需要采取许多额外的预防措施以避免一个线程导致自己被阻塞。


4
我认为你需要使用可重入锁来完成你想要做的事情。下面是来自http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html 的一段代码。
引用如下: 什么是可重入锁?简单来说,它与锁相关联的是获取次数。如果持有锁的线程再次获取该锁,则获取次数将增加,并且需要释放两次锁才能真正释放锁。这与 synchronized 的语义相似;如果一个线程进入由监视器保护的同步块,而该线程已经拥有该监视器,则该线程将被允许继续执行,并且当线程退出第二个(或更多)由该监视器保护的同步块时,锁不会被释放,只有在它退出受该监视器保护的第一个同步块时才会被释放。
虽然我没有尝试过,但我猜如果你想做上述的事情,你必须使用可重入锁。

2

Java似乎完全支持同一线程在一个对象上使用嵌套锁。这意味着,如果一个线程在一个对象上有外部和内部锁,并且另一个线程尝试在同一对象上加锁,则第二个线程将被挂起,直到第一个线程释放两个锁。

我的测试是在Java 6 SE下完成的。


在嵌套的同步块中,同一线程只会获取一个锁。 - Akash5288

1
在Java中,方法上的“synchronized”关键字基本上是在当前对象上同步,因此实际上它隐式地执行了您上面建议的操作。
如果在一个方法上对一个对象进行同步,然后在另一个方法上对同一个对象进行同步,您不会遇到任何问题,因为正如您所说,当前线程已经持有该对象的锁。

1
没有问题。在您的例子中,(一旦您修复代码以消除编译警告),同步确保method1和method2中的块不会同时执行。
这就是同步的目的。 :)

编辑:抱歉,我错过了你问题的一些部分,但是 Phill 已经回答了。总结一下,单个线程无法死锁。


0

不,如果第一个方法调用第二个方法,第二个方法不会停止。不会出现奇怪的结果(除了检查锁的轻微开销。这并不重要。从Java 6开始,JVM中有锁粗化 - Java SE 6性能白皮书。)

例如,看一下java.util.Vector的源代码。在同步方法内部有很多调用其他同步方法的调用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接