在哪些情况下我们需要同步一个方法?
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
。
假设我在 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
。