Java 可见性:构造后最终静态非线程安全集合发生变化
Java visibility: final static non-threadsafe collection changes after construction
我在 luaj 中找到了 following code snippet,我开始怀疑是否有可能在构建 Map
之后对它所做的更改可能对其他线程不可见,因为那里没有同步到位。
我知道,由于 Map
被声明为 final,其构造后的初始化值对其他线程可见,但是之后发生的变化呢。
有些人可能还意识到这个 class 不是线程安全的,以至于在多线程环境中调用强制甚至可能导致 HashMap
中的无限循环,但我的问题不是关于那。
public class CoerceJavaToLua {
static final Map COERCIONS = new HashMap(); // this map is visible to all threads after construction, since its final
public static LuaValue coerce(Object paramObject) {
...;
if (localCoercion == null) {
localCoercion = ...;
COERCIONS.put(localClass, localCoercion); // visible?
}
return ...;
}
...
}
你是对的,对 Map
的更改可能对其他线程不可见。每个访问 COERCIONS
(读和写)的方法都应该是同一个对象上的 synchronized
。或者,如果您永远不需要原子访问序列,则可以使用 synchronized collection.
(顺便说一句,你为什么要使用原始类型?)
两个选项:
// Synchronized (since Java 1.2)
static final Map COERCIONS = Collections.synchronizedMap(new HashMap());
// Concurrent (since Java 5)
static final Map COERCIONS = new ConcurrentHashMap();
它们各有优缺点。
ConcurrentHashMap
pro 无锁定。缺点是操作不是 atomic,例如一个线程中的 Iterator
和另一个线程中对 putAll
的调用将允许迭代器看到 一些 添加的值。
这段代码实际上很糟糕,可能会导致很多问题(可能不是无限循环,TreeMap
更常见,HashMap
更有可能由于覆盖或可能是一些随机异常)。你是对的,不能保证在一个线程中所做的更改会被另一个线程看到。
这里的问题可能看起来不是很大,因为这个 Map
用于缓存目的,因此静默覆盖或可见性滞后不会导致真正的问题(只有两个不同的强制实例将用于相同的 class,在这种情况下可能没问题)。然而,这样的代码仍然有可能破坏您的程序。喜欢的话可以提交补丁给LuaJ团队
我在 luaj 中找到了 following code snippet,我开始怀疑是否有可能在构建 Map
之后对它所做的更改可能对其他线程不可见,因为那里没有同步到位。
我知道,由于 Map
被声明为 final,其构造后的初始化值对其他线程可见,但是之后发生的变化呢。
有些人可能还意识到这个 class 不是线程安全的,以至于在多线程环境中调用强制甚至可能导致 HashMap
中的无限循环,但我的问题不是关于那。
public class CoerceJavaToLua {
static final Map COERCIONS = new HashMap(); // this map is visible to all threads after construction, since its final
public static LuaValue coerce(Object paramObject) {
...;
if (localCoercion == null) {
localCoercion = ...;
COERCIONS.put(localClass, localCoercion); // visible?
}
return ...;
}
...
}
你是对的,对 Map
的更改可能对其他线程不可见。每个访问 COERCIONS
(读和写)的方法都应该是同一个对象上的 synchronized
。或者,如果您永远不需要原子访问序列,则可以使用 synchronized collection.
(顺便说一句,你为什么要使用原始类型?)
两个选项:
// Synchronized (since Java 1.2)
static final Map COERCIONS = Collections.synchronizedMap(new HashMap());
// Concurrent (since Java 5)
static final Map COERCIONS = new ConcurrentHashMap();
它们各有优缺点。
ConcurrentHashMap
pro 无锁定。缺点是操作不是 atomic,例如一个线程中的 Iterator
和另一个线程中对 putAll
的调用将允许迭代器看到 一些 添加的值。
这段代码实际上很糟糕,可能会导致很多问题(可能不是无限循环,TreeMap
更常见,HashMap
更有可能由于覆盖或可能是一些随机异常)。你是对的,不能保证在一个线程中所做的更改会被另一个线程看到。
这里的问题可能看起来不是很大,因为这个 Map
用于缓存目的,因此静默覆盖或可见性滞后不会导致真正的问题(只有两个不同的强制实例将用于相同的 class,在这种情况下可能没问题)。然而,这样的代码仍然有可能破坏您的程序。喜欢的话可以提交补丁给LuaJ团队