同步语句 - 同步方法和同步语句是否等效?

3

对于这个简单的同步语句:

public void addName(String name) {
    synchronized(this) { // line1
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

似乎两个线程,假设为t1和t2,可以同时调用addName方法,但一旦到达标记为line1的行,只有一个线程能够继续执行,这意味着其他线程将被挂起。这意味着即使nameList放在同步语句之外,也保证了nameList不会被多个线程冲突。
这是真的吗?如果是,那么如果在同步语句之前不需要执行任何操作,以下方法是否有任何区别:
public void addName(String name) {
    synchronized(this) {  //line1
        lastName = name;
        nameCount++;

        nameList.add(name);
    }
}

或者:

public synchronized void addName(String name) {
        lastName = name;
        nameCount++;
        nameList.add(name);
}

我知道这一点确切的内容。
void synchronized add(){

}

等同于:

void add(){
  synchronized(this){

  }
}

我感到困惑的是,在addName的例子中,我认为执行顺序可能像这样:
t1:synchronized steatement
t1:nameList.add
t2:synchronized steatement
t2:nameList.add

这意味着在synchronized语句nameList.add之间,其他线程不会执行变更。因此,在同步块内或外放置nameList.add没有区别。但实际上,执行情况可能如@JB Nizet的答案所示:
t1:synchronized steatement
t2:synchronized steatement

t2:nameList.add
t1:nameList.add

nameList放在代码块内外都很重要。


它们是等价的(最后两个)。 - Maroun
哦,是的,我忘记了 :) - hguser
https://dev59.com/uHRB5IYBdhLWcg3wn4QL?rq=1有很多链接或相关问题,询问同步方法和块之间的区别。 - Oleg Estekhin
1个回答

4

这两个代码片段是等效的,但第一个不是。

在第一个片段中,由于列表添加不是同步块的一部分,因此两个线程可以同时执行此指令。如果该列表不是线程安全的,则存在问题。

即使列表是线程安全的,这也可能是一个问题,因为对状态的各个部分(计数、姓氏和列表)的更改不是原子性的。因此,其他线程可能会看到最后一个名称的新值,但找不到该名称在列表中的位置。


谢谢,我明白了。我一直以为一个线程在同步语句完成后立即调用nameList.add,而没有其他线程可以插入。但实际上,nameList.add可以同时访问。 - hguser

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