我正在尝试理解Java中的并发性。我知道synchronized可以在对象上创建监视器,这样其他线程就不能对该对象执行操作。Volatile与处理器缓存有关,如果使用它,则所有线程不会创建对象的副本。因此,在我的想法中,如果我运行此代码,我将获得正确的计数器值(40000)。但是实际上我得到的是错误的结果!
public class Main {
private static volatile Integer counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Counter();
Thread thread2 = new Counter();
thread.start();
thread2.start();
thread.join();
thread2.join();
System.out.println(counter);
}
public static class Counter extends Thread{
@Override
public void run() {
for (int i = 0; i < 20000; i++) {
synchronized (counter){
counter++;
}
}
}
}
}
但是,如果我使用同步方法,我将获得正确的结果:
public class Main {
private static volatile Integer counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Counter();
Thread thread2 = new Counter();
thread.start();
thread2.start();
thread.join();
thread2.join();
System.out.println(counter);
}
public synchronized static void increment(){
counter++;
}
public static class Counter extends Thread{
@Override
public void run() {
for (int i = 0; i < 20000; i++) {
increment();
}
}
}
}
所以问题是 - 为什么在 Integer 对象上使用 synchronized 不起作用?
Integer
是一个不可变类型,而counter++
实际上是将一个引用分配给不同的Integer
对象。然后duplink
提供了答案。 - Stephen Ccounter
也是volatile
的事实对此没有影响。 - Stephen Csynchronized(x)
,然后将不同的对象引用分配给x
。但是,_这个_问题的作者在任何地方都没有这样做。 - Solomon Slowvolatile
不起作用是因为counter++
不是原子操作。counter++
首先读取 volatile 计数器变量的值,然后计算新值,最后写回新值。volatile
无法防止两个线程同时读取相同的值,同时计算相同的新值,然后同时写入相同的新值。结果是,尽管两个不同的线程都增加了计数器,但计数器只增加了一次。 - Solomon Slow