Java同步计数器 - get()怎么样?

时间:2012-10-04 16:15:26

标签: java multithreading synchronization counter

众所周知,简单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()。我想知道它是否会使它具有线程安全性,但如果许多线程正在读取值并且很少更改它,那么它也会更有效。

4 个答案:

答案 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关键字会在这里运作吗?

是的,我相信会的。