众所周知,简单x++
不是原子操作,实际上是读 - 增 - 写操作。这就是它应该同步的原因。但是get()
呢?我已经读过它应该同步,但有人可以解释我为什么?通过引入happens-before
关系来避免内存一致性错误?
如果多个线程经常调用get()
并且很少更改值,那么情况怎么样? synchronized get()
是不是放慢了速度?在该场景中是否有其他方法可以实现同步(不使用AtomicInteger)? volatile
关键字会在这里发挥作用吗?
public class Counter {
private int value;
public synchronized int get() { return value; }
public synchronized int increment() { return ++value; }
public synchronized int decrement() { return --value; }
}
谢谢!
编辑:
我想说清楚。使用volatile
我的意思是介绍该关键字并删除synchronized
方法中的get()
。我想知道它是否会使它具有线程安全性,但如果许多线程正在读取值并且很少更改它,那么它也会更有效。
答案 0 :(得分:10)
在该场景中是否还有其他方法可以实现同步(不使用AtomicInteger)?
首先,如果可以,您应该使用AtomicInteger
。我不确定你为什么不使用它。
volatile关键字会在这里运作吗?
是,++
除外。 AtomicInteger
提供安全增量而不锁定。如果你想自己滚动(出于一些疯狂的原因),那么你将需要阻止,或者你需要复制AtomicInteger
内部旋转机制。
但是get()怎么样?我已经读过它应该同步,但有人可以解释我为什么?
AtomicInteger
包装volatile int
以提供其功能。当您访问volatile
字段时,您也会在get()
上穿过内存屏障。您需要跨越该内存屏障以确保如果另一个线程更新了该值,则调用get()
的线程会看到更新。否则,线程可能正在使用过时的值。
答案 1 :(得分:1)
如果您反复读取非易失性值,JVM可以将值缓存在寄存器中,您可能看不到任何更改。
避免这种情况的方法是使值变为volatile。
但是,如果需要考虑性能,请使用无锁的AtomicInteger。
答案 2 :(得分:0)
但是get()怎么样?我读过它也应该同步,但是 有人可以解释我为什么吗?
假设get()
未同步,那么任何线程都可以在线程执行set()
操作时调用它。
现在,如果假设,线程B正在执行set()操作以将字段a
的值设置为5.现在,假设两个线程正在读取值a ..一个线程在设置操作之前读取完成后,其他线程在设置操作后读取..
因此,两个线程都有不同的值。因此它们具有不一致的字段a
状态。
现在假设get()
方法是同步的..然后如果一个线程正在设置一个值..那么除非get()
操作完成,否则没有线程可以调用set()
操作。所以每个线程都会获得相同的值..
答案 3 :(得分:0)
但是get()怎么样?我已经读过它应该同步,但有人可以解释我为什么?通过引入先发生关系来避免内存一致性错误?
是的,它应该是同步的。如果不是,它可能正在使用陈旧值,就像增量/减量一样。
多线程经常调用get()并且很少更改值时的情况如何?是不是同步get()减慢它们?
无可争议的锁非常便宜。即使对于并发获取,成本也可能不会对整体性能产生太大影响。 volatile
在这里工作,但出于性能原因而不使用AtomicInteger
可能没有必要。
在该场景中是否还有其他方法可以实现同步(不使用AtomicInteger)? volatile关键字会在这里运作吗?
是的,我相信会的。