我有一个复杂的映射,它不需要锁定频繁获取和不频繁的更改,因为这些键唯一地确定了它们引用元素的行为。
也就是说,如果键是相同的,它引用的结构将始终具有相同的行为。实际上,密钥是不可变的final,引用的值实际上是final和/或线程安全。
假设我对自定义hashmap中的底层volatile引用数组有一个volatile参考。我对这个数组的hashmap算法使用ar.length成员作为模数,所以如果在get函数中,调用者看到一个非当前数组,它的所有引用仍然是好的,并且仍然服从散列槽,所以如果它在没有互斥锁的情况下成功获得非空值,这是正确的值。
我的计划是每当一个get失败时,调用者然后为key构造正确的值,一个put相对于其他put进行锁定并将一个对象填充到数组中。在退出临界区之前,put代码将volatile数组字段“ar”重新分配给自身,希望作为消息发送给编译器和热点编译器,以创建相对于使用volatile数组引用查找散列值的gets的fence。
只要编译器没有使用“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)
}
答案 0 :(得分:2)
无法优化易失性写入,所以是的,您可以在这里获得内存保证。而且,从数组中读取值(至少在概念上)意味着您读取数组的volatile变量,您应该获得一个保证易失性读取。
所以虽然这应该有用 - 如果您正在使用Hotspot,通常的方法是使用sun.misc.Unsafe
- 您可以查看Java5中的并发集合,其中经常展示该模式。 (是的,我们都可以期待在未来获得易变元素阵列 - 但是Doug Lea和Co正在制定一个规范,但不知道它们有多远。)
虽然问题是为什么你自己实现这个 - 但Cliff的非阻塞hashmap有一些非常强的正确性保证(afaik他们用CHESS检查了一个,很多人看过与JDK的ConcurrentHashMap相比,它具有出色的性能。
当然比同步put
操作更快。