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

jutyujz0  于 2021-06-29  发布在  Java
关注(0)|答案(2)|浏览(309)

假设我有以下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是synchronizedcounter类的两个不同示例。真的需要同步方法增量吗?因为我知道,当我们使用同步方法时,线程在对象上持有一个锁,这样其他线程就无法获得同一对象上的锁,但是与其他对象“关联”的线程可以毫无问题地执行该方法。现在,因为我只有一个线程与对象c1相关联,所以需要使用synchronized方法吗?如果不存在与同一对象关联的其他线程?

6kkfgxo0

6kkfgxo01#

在你的例子中, synchronized 不需要,因为每个线程都有自己的类示例,所以它们之间没有数据“共享”。
如果您将示例改为:

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

然后需要同步该方法,因为 ++ 操作不是原子的,示例在线程之间共享。
底线是你的类没有线程是不安全的 synchronized . 如果您从未跨线程使用单个示例,那么这并不重要。对于线程不安全的类,有很多合法的用例。但是一旦你开始在线程之间共享它们,所有的赌注都被取消了(例如,恶意的bug可能会随机出现)。

emeijp43

emeijp432#

给定的代码/示例不需要同步,因为它使用两个不同的示例(以及变量)。但是,如果两个或多个线程之间共享一个示例,则需要同步,尽管注解中另有说明。
实际上,创建一个程序来显示该行为非常简单:
远离的 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 不管怎样。

相关问题