5

这是一个简单的例子:

private long counter = 0;

// note this method is NOT synchronized
// this will be called by thread A
public void increment() { counter++; }

// note this method IS synchronized
// this will be called by thread B
public synchronized long value() { return counter; }

所以我只想获得一个好的值counter,而不是 cpu 缓存中的卡住值,因为该变量是非易失性的。目标是不使计数器易失,因此它不会影响执行增量的线程 A,而只会影响线程 B,我不在乎,当它读取变量时。

只是为了记录,我计划counter在线程 A 已经完成时从线程 B 读取值......

4

4 回答 4

3

不,线程 B 中的同步块并不能确保它会读取 的实际当前值counter。您将需要两个线程中的同步块来执行此操作。从实际的角度来看,您的代码确保运行线程 B 的处理器使其缓存无效并counter从主内存中读取 的值,但它不能确保运行线程 A 的处理器将其当前值刷新到主内存,因此 main记忆可能过时了。

由于在两个线程中使用 volatile 变量比同步块便宜,因此使用countervolatile 可能是正确的解决方案。这就是 volatile 变量的用途。

编辑:如果线程 A 将在线程 B 读取最终值之前完成,您可以将线程 A 的整个执行包含在一个同步块中,或者在读取计数器之前让线程 B 加入线程 A,确保线程 A 在计数器被读取。这将导致在线程 A 执行结束时刷新一次缓存,这对性能的影响可以忽略不计。

于 2014-03-28T07:36:02.797 回答
2

long赋值不能保证是原子的,所以 B 不仅可以读取一个陈旧的值,它还可以读取一半的写入值。

为了获得适当的可见性,您需要使 counter volatile。请注意,即使这样,从多个线程调用 increment n 次也可能不会将 counter 增加 n。

你可以简单地使用AtomicLongto ,因为你的问题。

于 2014-03-28T07:35:08.120 回答
1

不,synchornized只保证在同一个锁的同步块中所做的更改的可见性:

synchornized(this) {
    counter++;
}

或在它们之前(由happens-before关系的传递性质定义):

// Thread A
counter++
synchronized (this) {
    finished = true; 
}

// Thread B
synchonized (this) {
    if (finished) {
        // you can read counter here
    }
}

但是请注意,counter如果您在确定线程 A 已完成(例如,使用join())后阅读它,则保证是可见的:

threadA.join();
// you can read counter here
于 2014-03-28T07:41:59.420 回答
0

不,不能保证总是Thread B给出最新的值。因为increment()是非同步方法,而 value() 是同步方法。
自从

当线程在对象的同步方法中时,所有其他希望执行此同步方法或对象的任何其他同步方法的线程都必须等待。

此限制不适用于已经拥有锁并正在执行对象的同步方法的线程。这样的方法可以调用对象的其他同步方法而不会被阻塞。对象的非同步方法当然可以随时被任何线程调用。

于 2014-03-28T07:36:41.987 回答