3

众所周知,simplex++不是原子操作,实际上是读-增量-写操作。这就是为什么它应该同步。但是呢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我的意思是引入该关键字并synchronizedget()方法中删除。我想知道它是否会使其成为线程安全的,但如果许多线程正在读取该值并且一个很少更改它,它是否也会更有效。

4

4 回答 4

10

在这种情况下是否有其他方法可以实现同步(不使用 AtomicInteger)?

首先,如果可以的话,你应该使用AtomicInteger。我不确定你为什么不使用它。

volatile 关键字在这里有用吗?

是的,除了++. AtomicInteger提供无锁定的安全增量。如果您想自己滚动(出于某种疯狂的原因),那么您将需要阻塞,或者您需要复制AtomicInteger内部自旋机制。

但是 get() 呢?我读过它也应该同步,但有人能解释一下为什么吗?

AtomicInteger包装 avolatile int以提供其功能。当您访问该volatile字段时,您也会跨越内存障碍get()。您需要跨越该内存屏障以确保如果另一个线程已更新该值,则调用该值的线程会get()看到更新。否则,线程可能正在使用过时的值。

于 2012-10-04T16:16:24.750 回答
1

如果您反复读取一个非易失性值,JVM 可以将该值缓存在一个寄存器中,您可能看不到任何更改。

避免这种情况的方法是使值不稳定。

但是,如果性能是一个问题,请使用无锁的 AtomicInteger。

于 2012-10-04T16:19:19.587 回答
0

但是 get() 呢?我读过它也应该同步,但有人能解释一下为什么吗?

假设如果get()没有同步,那么任何线程都可以在线程执行set()操作时调用它。

现在,如果假设,线程 B 正在执行 set() 操作以将字段的值设置a为 5。现在,假设两个线程正在读取值 a.. 一个线程在 set 操作完成之前读取,另一个线程在 set 操作完成之后读取设置操作..

因此,两个线程都有不同的值。因此它们的 field 状态不一致a

现在假设该get()方法是同步的..那么如果一个线程正在设置一个值..那么get()除非set()操作完成,否则没有线程可以调用操作..所以每个线程都会得到相同的值..

于 2012-10-04T16:21:20.043 回答
0

但是 get() 呢?我读过它也应该同步,但有人能解释一下为什么吗?通过引入happens-before关系来避免内存一致性错误?

是的,它应该是同步的。如果不是,它可能使用了一个陈旧的值,就像增量/减量一样。

当 get() 经常被多个线程调用并且值很少更改时,情况会怎样。同步的 get() 不会减慢它们的速度吗?

无争议的锁非常便宜。即使对于并发获取,成本也可能不会对整体性能产生太大影响。volatile在这里工作,但AtomicInteger出于性能原因不使用可能没有必要。

在这种情况下是否有其他方法可以实现同步(不使用 AtomicInteger)?volatile 关键字在这里有用吗?

是的,我相信会的。

于 2012-10-04T16:21:44.783 回答