【发布时间】:2016-10-28 07:33:05
【问题描述】:
Java并发实战有如下例子:
@ThreadSafe
public class VolatileCachedFactorizer implements Servlet {
private volatile OneValueCache cache = new OneValueCache(null, null);
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = cache.getFactors(i);
if (factors == null) {
factors = factor(i);
cache = new OneValueCache(i, factors);
}
encodeIntoResponse(resp, factors);
}
}
}
OneValueCache 是不可变的。
据我了解,使用 volatile 可确保所有线程都能看到存储在 cache 变量中的最新引用。
同时它说
任何线程都可以安全地使用不可变对象,而无需额外的 同步,即使不使用同步来发布它们。
对我来说,它表明我们实际上不需要上面用于同步的volatile。
JLS也有如下例子:
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
他们不使用volatile 字段f。不是说其他线程可以看到f为null,而永远看不到创建的实例吗?
谁能解释一下?
【问题讨论】:
-
对象不可变,变量不可变。
-
你把两个非常不同的东西混为一谈: - 一个对象,如果被指定为不可变的,那么它是线程安全的。 - 一个变量(指一个对象)可以指向任何其他对象,因此需要
volatile关键字来确保使用该变量的任何组件都可以访问该变量所指的任何内容的最新副本 -
让我感到困惑的是
Immutable objects can be published through any mechanism.分配给变量是一种机制,但这样做不是线程安全的,对吧? -
因为我们使用 volatile 我们也可以在第一个示例中使用有效的可变类。 volatile 保证构造不会被重新排序并分配给 volatile。阅读线程也会看到它。
标签: java concurrency java-memory-model