有人能解释为什么这个例子没有易失性的线程安全吗?
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
实际上,假设computeHashCode函数总是返回相同的结果,并且没有副作用(即幂等),那么甚至可以消除所有的同步。
// Lazy initialization 32-bit primitives
// Thread-safe if computeHashCode is idempotent
class Foo {
private int cachedHashCode = 0;
public int hashCode() {
int h = cachedHashCode;
if (h == 0) {
h = computeHashCode();
cachedHashCode = h;
}
return h;
}
// other functions and members...
}更多:我明白了,我们不关心这个值是否计算了两次(所以它并不是真正的线程安全)。我还想知道,在计算了哈希码之后创建的新线程是否保证看到了新的哈希码?
发布于 2012-02-01 21:21:32
这是在薄冰上行走,但这是一个解释。可见性问题意味着一些线程可能会看到旧版本和一些新版本。在我们的例子中,一些线程看到0,而另一些线程- cachedHashCode。
调用hashCode()并查看cachedHashCode的线程只会返回它(不满足if (h == 0)条件),一切都正常。
但是看到0的线程(尽管可能已经计算了cachedHashCode )将重新计算它。
换句话说,在最坏的情况下,每个线程都将第一次进入分支查看0 (比如如果它是ThreadLocal)。
由于computeHashCode()是幂等的(非常重要),所以无论是多次调用它(通过不同的线程)并将它重新分配给相同的变量,都不会产生任何副作用。
发布于 2012-02-01 21:17:43
这里的重要信息是
computeHashCode函数总是返回相同的结果
如果这是真的,那么已知的computeHashCode实际上是不可变的,而且由于它总是相同的值,所以永远不会出现并发问题。
让cachedHashCode变得不稳定。就线程安全性而言,这不会有什么影响,因为您总是分配并返回一个线程局部变量h,它将是一个非零的computedHashCode。
发布于 2012-02-01 21:28:22
这就是所谓的活泼的单一检查成语。当计算一个值是幂等的时候(每次返回相同的值;推论:类型必须是不可变的)和廉价(如果它被意外地重新计算了一次,那就可以了)。这总是以某种形式出现在
class Foo {
private Value cacheField; // field holding the cached value; not volatile!
public Value getValue() {
Value value = cacheField; // very important!
if (value == 0 or null or whatever) {
value = computeValue();
cacheField = value;
}
return value;
}
}或者差不多等同的东西。如果您的实现不是幂等的,或者不是廉价的,那么您应该使用另一个成语;有关详细信息,请参阅有效的Java项目71。但问题是,线程最多只读取一个cacheField,如果它们看到cacheField处于未计算值的状态,则会重新计算该值。
例如,正如在有效Java中提到的,String.hashCode()就是这样实现的。
https://stackoverflow.com/questions/9103449
复制相似问题