从另一个同步方法调用同步方法是否安全?

96

如果一个同步方法调用另一个同步方法,它是否线程安全?

void synchronized method1() {
     method2()
}

void synchronized method2() {
}

这篇文章能帮助回答你的问题吗?或者你还有什么疑惑?http://kalyanchakravarthy.net/?p=413 - James Black
是的 - 假设只在上下文中调用method2,您实际上不需要将其标记为同步。 - debracey
4
同时,它是否线程安全将取决于这两种方法中发生的情况。例如,如果它们调用非线程安全列表,则可能不是线程安全的,如果某个其他线程可以修改该集合。 - James Black
1
作为对我猜测的实际问题的回答:是的,synchronized关键字使用递归锁;您可以安全地从另一个同步方法调用同步方法。 - Brett Kail
2
已经有一段时间了,但它仍然是谷歌上的首个搜索结果,所以:是的,在同一对象上的同步块/方法是可重入的。https://dev59.com/r2fWa4cB1Zd3GeqPlu72 - Szocske
1
参见:https://softwareengineering.stackexchange.com/q/286374/49758 - Joshua Goldberg
3个回答

116

是的,当您将方法标记为synchronized时,实际上是这样做的:

void method1() {
    synchronized (this) {
        method2()
    }
}

void method2() {
    synchronized (this) {
    }
}

当线程从方法1调用进入方法2时,它会确保持有锁定的this,因为它已经拥有该锁,所以它可以直接通过。

现在当另一个线程尝试直接进入方法1或方法2时,它将被阻塞,直到它获取锁(this),然后才能进入其中任何一个方法。

正如James Black在评论中指出的那样,您必须注意方法体内部的操作。

private final List<T> data = new ArrayList<T>();

public synchronized void method1() {
    for (T item : data) {
        // ..
    }
}

public void method3() {
    data.clear();
}

由于method3未同步,因此当线程B在method1中工作时,线程A可能会调用method3,从而导致线程不安全,并出现ConcurrentModificationException


我正在尝试回答一个几乎与此处提出的问题相同的问题。这是两个可能的答案(另外两个说它不会运行),哪个是正确的?C. 代码将运行,但存在潜在的死锁情况 D. 代码将正常运行,因为Java提供了可重入同步,使线程能够多次获取相同的锁 ----- 我猜是D,但也许潜在的死锁情况取决于方法体? - user3140993
@user3140993 这里的代码没有死锁的机会。method3 显示了不安全的线程操作,但你对可重入同步的理解是正确的。 - pickypg

8

一个用synchronized标记的方法调用另一个用synchronized标记的方法,这个类是否线程安全。

通常情况下,无法确定。这取决于方法的功能以及同一和其他类上的其他方法的功能。

但是,我们可以确定,不同线程在相同对象上对method1和method2的调用不会同时执行。根据方法的功能,这可能足以说明该类关于这些方法是线程安全的。


3

来自Java教程网站http://download.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

  1. 在同一个对象上调用同步方法时,两个synchronized方法的执行不可能交错进行。当一个线程正在执行对象的同步方法时,所有其他调用该对象的同步方法的线程都被阻塞(暂停执行),直到第一个线程完成为止。

  2. 同步方法退出时,它会自动与后续对相同对象调用的任何同步方法建立happens-before关系。这确保了对象状态的更改对所有线程都是可见的。

Java 会确保如果有2个线程执行相同的方法,方法不会同时执行,而会一个接着一个执行。

但你需要注意活性问题,http://download.oracle.com/javase/tutorial/essential/concurrency/starvelive.html

而且你也需要注意是否过度使用锁定,因为在你所使用的代码中this会锁定整个对象,如果你的对象只需要同步访问一个变量,你应该只锁定那个变量即可。


@Stephen Lee - 你不能锁定一个变量。当你使用synchronized (this.someVar)时,你是在查找someVar所持有的对象的引用。这个区别非常重要。 - Stephen C
不将锁定放在“this”上的示例。private final Object mSync; synchronized (this.mSync) {//some code} - zeitgeist

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