在哪些情况下我们需要同步一个方法?

In which cases we need to synchronize a method?

假设我在 Java

中有以下代码
public class SynchronizedCounter {
    private int c = 0;

    public synchronized void increment() {
        c++;
    }
}

然后我创建了两个线程 T1 和 T2

Thread T1 = new Thread(c1);
Thread T2 = new Thread(c2);

其中 c1 和 c2 是 class SynchronizedCounter 的两个不同实例。 真的需要同步方法增量吗?因为我知道当我们使用同步方法时,线程持有对象的锁,这样其他线程就无法获得同一对象的锁,但是与其他对象“关联”的线程可以毫无问题地执行该方法。现在,因为我只有一个线程与对象 c1 相关联,所以无论如何都需要使用同步方法?另外如果不存在与同一对象关联的其他线程?

在您的具体示例中,不需要 synchronized,因为每个线程都有自己的 class 实例,因此它们之间没有数据“共享”。

如果您将示例更改为:

Thread T1 = new Thread(c);
Thread T2 = new Thread(c);

然后你需要同步方法,因为++操作不是原子的,实例是线程间共享的。

最重要的是,如果没有 synchronized,您的 class 就不是线程安全的。如果您从不跨线程使用单个实例,那也没关系。 classes 有很多非线程安全的合法用例。但是一旦你开始在线程之间共享它们,所有的赌注都会被取消(即恶性错误可能会随机出现)。

Given code/example 不需要同步,因为它使用两个不同的实例(因此,变量)。但是,如果您有一个实例在两个或多个线程之间共享,则需要同步,尽管注释另有说明。

实际上,创建一个程序来显示该行为非常简单:

  • 已删除 synchronized
  • 添加了从两个线程调用方法的代码
public class SynchronizedCounter {
    private int c = 0;

    public void increment() {
        c++;
    }

    public static void main(String... args) throws Exception {
        var counter = new SynchronizedCounter();
        var t1 = create(100_000, counter);
        var t2 = create(100_000, counter);
        t1.start();
        t2.start();
        // wait termination of both threads
        t1.join();
        t2.join();
        System.out.println(counter.c);
    }

    private static Thread create(int count, SynchronizedCounter counter) {
        return new Thread(() -> {
            for (var i = 0; i < count; i++) {
                counter.increment();
            }
            System.out.println(counter.c);
        });
    }
}

最终(经常?)这将导致 奇怪的 数字,例如:

C:\TMP>java SynchronizedCounter.java
122948
136644
136644

添加 synchronized 并且输出应始终以 200000:

结尾
C:\TMP>java SynchronizedCounter.java
170134
200000
200000

显然发布的代码不完整:增量变量是 private 并且没有检索增量值的方法。不可能真正知道该方法是否必须是 synchronized