我有一个复杂的映射,不需要为频繁获取和不频繁更改锁定,因为键唯一地确定其引用元素的行为。
也就是说,如果键是相同的,它所引用的结构将始终具有相同的行为。事实上,键是不可变的 final,引用的值是有效的 final 和/或线程安全的。
假设我在我的自定义 HashMap 中有一个对底层 volatile 引用数组的 volatile 引用。我的这个数组的 hashmap 算法使用 ar.length 成员作为模数,所以如果在 get 函数中,调用者看到一个非当前数组,它的所有引用仍然是好的,并且仍然服从哈希槽,所以如果它在没有互斥量的情况下成功获得非空值,这是正确的。
我的计划是,每当 get 失败时,调用者都会为键构造正确的值,然后执行一个 put,该 put 相对于其他 put 进行锁定,并将一个对象填充到数组中。就在退出临界区之前,put 代码将 volatile 数组字段“ar”重新分配给自身,希望作为给编译器和热点编译器的消息,以创建相对于使用 volatile 数组引用查找散列值的 gets 的栅栏。
只要编译器不 nop "ar = ar"赋值,这就可以工作:
private volatile Object[] ar;
public Object get (Object key)
{
Object[] ar = this.ar;
// get the value from the correct hash slot
}
public synchronized Object put (Object key, Object val) {
{
... stuff a new object into the correct hash slot
ar = ar; // will the compiler truly compile this statement with
// normal volatile fencing relative to the get function??
... if a race condition causes a value to already be mapped, return and use that
... rather than the one we created and passed to put (different than normal put
... contract)
}
最佳答案
无法优化 volatile 写入,所以是的,您将在这里获得内存保证。并且由于从数组中读取值也(至少在概念上)意味着您读取数组的 volatile 变量,因此您也应该获得一个有保证的 volatile 读取。
因此,虽然这应该可行 - 如果您正在使用 Hotspot,通常的方法是使用 sun.misc.Unsafe
- 您可以查看 Java5 向上的并发集合,其中该模式是经常证明。 (是的,我们都可以期待在未来获得可变元素数组——afaik Doug Lea 和 Co 正在为此制定规范,但不知道他们有多远。)
虽然问题是为什么你自己实现这个 - Cliff 的非阻塞 hashmap 有一些非常强大的正确性保证(afaik 他们用 CHESS 检查了一个并且很多人已经看过底层状态机)并且性能比较出色从 JDK 到 ConcurrentHashMap。
而且肯定比同步 put
操作更快。
关于java: volatile数组发布数据变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8873170/