2

最近我阅读了“Java 并发实践”部分 -->“3.4.2 示例:使用 volatile 发布不可变对象”。

然而; 我不能安静地理解它。这是情况!

不可变对象是:

@Immutable
class OneValueCache {
    private final BigInteger lastNumber;
    private final BigInteger[] lastFactors;
    public OneValueCache(BigInteger i, BigInteger[] factors) {
        lastNumber = i;
        lastFactors = Arrays.copyOf(factors, factors.length);
    }
    public BigInteger[] getFactors(BigInteger i) {
        if (lastNumber == null || !lastNumber.equals(i))
            return null;
        else
            return Arrays.copyOf(lastFactors, lastFactors.length);
    }
}

假设我们有一个这样的 servlet

public class MyServlet {
private volatile OneValueCache immutableValue;
.
.

public void service(){
            .
            // what this function do : gets value from immutable object increments its
            // value and creates new Immutable object and put it back.
            // 
            .
    immutableValue = new OneValueCache( lastNumber, lastFactor[]);
    .
    .
    .
}
.
.
.
}

根据这本书 MyServlet 是线程安全的,因为它使用 volatile 关键字发布不可变对象!

不过我觉得这里有问题!

认为:

线程 1 在时间 0 将值从 immutableValue 读取到其堆栈 var;

线程 2 在时间 0 将值从 immutableValue 读取到其堆栈 var;

线程 1 在时间 1 中增加堆栈变量中的值并创建新的 OneValueCache 对象 - 它是不可变的 - 并将其放入 immutableValue 字段变量中;

线程 2 在时间 2 中增加堆栈变量中的值并创建新的 OneValueCache 对象 - 它是不可变的 - 并将其放入 immutableValue 字段变量中;

这样我们就丢失了 Thread1 更新,所以这里没有 ThreadSafty!

相反,书上说:

“这种由不变量关联的多个状态变量的不可变持有者对象与用于确保其及时可见性的可变引用的组合允许 VolatileCachedFactorizer 即使没有显式锁定也是线程安全的。”

谁能帮我我在这里想念什么?

ps:为了更清楚,我知道数据一致性在这里举行。我们不能有无效的 OneValueCache 状态。但正如我的线程序列所示,这里有可能丢失更新。所以这个类不是线程安全的!

4

1 回答 1

4

不变性意味着线程安全,因为一旦创建,就没有线程可以修改它,因此值将在线程之间保持一致。

在上面的例子中,“private volatile OneValueCache immutableValue”一次只能保存一个值,所以如果线程 1 先更新,然后线程 2 更新参考值,那么最后一个值将由“onevaluecache”表示,线程 1 的更新将丢失因为您只存储一个参考值。

您提到的步骤中 OneValueCache 的状态绝不是不一致的,因此它是线程安全的。

编辑:状态不一致的一些情况是:

  1. 如果 immutableValue 变量不是 volatile 则每个线程将存储自己的副本,这意味着缓存将无法正常工作。

  2. 如果 OneValueCache 类是可变的,那么多个线程将同时更改其值,这意味着状态将不一致,因此不是线程安全的。

于 2013-04-20T12:02:15.007 回答