什么时候应该将方法声明为同步的?
When should a method be declared synchronized?
以下是一个简单的工作程序,它使用两个线程打印一个计数器:
public class SynchronizedCounter implements Runnable {
private static int i = 0;
public void increment() { i++; }
public int getValue() { return i; }
@Override
public void run() {
for( int i = 0; i < 5; i++ ) {
synchronized( this ) {
increment();
System.out.println( Thread.currentThread().getName() + " counter: " + this.getValue() );
}
}
}
public static void main( String[] args ) {
ExecutorService executorService = Executors.newFixedThreadPool( 2 );
SynchronizedCounter synchronizedCounter = new SynchronizedCounter();
executorService.submit( synchronizedCounter );
executorService.submit( synchronizedCounter );
executorService.shutdown();
}
}
输出符合预期 - 两个线程按顺序显示计数器。
如果 increment()
和 getValue()
被声明为 synchronized
并且 synchronized
块代码被注释,输出表明可见性和竞争问题。
因为 increment()
和 getValue()
没有被声明为 synchronized
并且如果可以实现同步,只需使用 synchronized
块(传递监视器对象) , 在什么情况下应该声明为 synchronized
?
当您需要方法主体周围的同步块的语义时,声明一个同步方法。
synchronized void increment() {
...
}
完全相同:
void increment() {
synchronized (this) { ... }
}
在上面的代码中这样做的事情是你不再自动执行 increment()
和 getValue()
方法:另一个线程可以插入并在你的调用之间执行这些方法线程:
Thread 1 Thread 2
increment()
increment()
getValue()
getValue()
这是可以的,但是两个线程不能同时执行increment()
(或getValue()
),因为它们是同步的(*)。
另一方面,在问题的代码中围绕调用进行同步意味着这两个调用是原子执行的,因此两个线程不能交错:
Thread 1 Thread 2
increment()
getValue()
increment()
getValue()
(*) 实际上,它们可以同时执行该方法。只是除了一个线程之外的所有线程都将在 synchronized (this)
.
处等待
在您的代码中,class 本身不是线程安全的,由客户端(您的 run
方法)来确保线程安全。
如果你想让 class 线程安全,这样客户端不需要做任何特别的事情来使用它,它应该有适当的同步机制并提出 "higher level" 方法可以 运行 必要时对操作组合进行原子操作。
例如,想想 AtomicInteger
,它有一个 incrementAndGet()
方法,可以提供更好的方法。您的 run
方法将变为:
for( int i = 0; i < 5; i++ ) {
int newValue = counter.incrementAndGet();
System.out.println(Thread.currentThread().getName() + " counter: " + newValue );
}
您的 increment()
方法使用了 i++
,它实际上被 3 条指令所取代。如果该方法未声明为 synchronized
,则多个线程可能同时执行它,这将导致竞争条件和不正确的结果。
If increment() and getValue() are declared as synchronized and
synchronized block code is commented, the output manifests visibility
and race problems.
在这种情况下没有竞争条件。您会看到不同的结果,因为现在 increment()
和 getValue()
不是原子执行的(原子地我的意思是两种方法作为一个同步块),而一个线程调用 increment()
并且即将调用 getValue()
另一个线程也称为 increment()
。
因此,如果您打算在 run
方法的 synchronized
块之外调用它们,则需要使 increment()
和 getValue()
同步(因为它们是声明为 public
可以进行此类调用)。
以下是一个简单的工作程序,它使用两个线程打印一个计数器:
public class SynchronizedCounter implements Runnable {
private static int i = 0;
public void increment() { i++; }
public int getValue() { return i; }
@Override
public void run() {
for( int i = 0; i < 5; i++ ) {
synchronized( this ) {
increment();
System.out.println( Thread.currentThread().getName() + " counter: " + this.getValue() );
}
}
}
public static void main( String[] args ) {
ExecutorService executorService = Executors.newFixedThreadPool( 2 );
SynchronizedCounter synchronizedCounter = new SynchronizedCounter();
executorService.submit( synchronizedCounter );
executorService.submit( synchronizedCounter );
executorService.shutdown();
}
}
输出符合预期 - 两个线程按顺序显示计数器。
如果 increment()
和 getValue()
被声明为 synchronized
并且 synchronized
块代码被注释,输出表明可见性和竞争问题。
因为 increment()
和 getValue()
没有被声明为 synchronized
并且如果可以实现同步,只需使用 synchronized
块(传递监视器对象) , 在什么情况下应该声明为 synchronized
?
当您需要方法主体周围的同步块的语义时,声明一个同步方法。
synchronized void increment() {
...
}
完全相同:
void increment() {
synchronized (this) { ... }
}
在上面的代码中这样做的事情是你不再自动执行 increment()
和 getValue()
方法:另一个线程可以插入并在你的调用之间执行这些方法线程:
Thread 1 Thread 2
increment()
increment()
getValue()
getValue()
这是可以的,但是两个线程不能同时执行increment()
(或getValue()
),因为它们是同步的(*)。
另一方面,在问题的代码中围绕调用进行同步意味着这两个调用是原子执行的,因此两个线程不能交错:
Thread 1 Thread 2
increment()
getValue()
increment()
getValue()
(*) 实际上,它们可以同时执行该方法。只是除了一个线程之外的所有线程都将在 synchronized (this)
.
在您的代码中,class 本身不是线程安全的,由客户端(您的 run
方法)来确保线程安全。
如果你想让 class 线程安全,这样客户端不需要做任何特别的事情来使用它,它应该有适当的同步机制并提出 "higher level" 方法可以 运行 必要时对操作组合进行原子操作。
例如,想想 AtomicInteger
,它有一个 incrementAndGet()
方法,可以提供更好的方法。您的 run
方法将变为:
for( int i = 0; i < 5; i++ ) {
int newValue = counter.incrementAndGet();
System.out.println(Thread.currentThread().getName() + " counter: " + newValue );
}
您的 increment()
方法使用了 i++
,它实际上被 3 条指令所取代。如果该方法未声明为 synchronized
,则多个线程可能同时执行它,这将导致竞争条件和不正确的结果。
If increment() and getValue() are declared as synchronized and synchronized block code is commented, the output manifests visibility and race problems.
在这种情况下没有竞争条件。您会看到不同的结果,因为现在 increment()
和 getValue()
不是原子执行的(原子地我的意思是两种方法作为一个同步块),而一个线程调用 increment()
并且即将调用 getValue()
另一个线程也称为 increment()
。
因此,如果您打算在 run
方法的 synchronized
块之外调用它们,则需要使 increment()
和 getValue()
同步(因为它们是声明为 public
可以进行此类调用)。