Reputation: 745
I have a complex map, which doesn't need locking for the frequent gets and infrequent changes, because the keys uniquely determine the behavior of their referenced elements.
That is, if the key is the same, the structure it references will always have the same behavior. In fact, the key is immutable final and the referenced value is effectively final and/or threadsafe.
Lets say I have a volatile reference to the underlying volatile array of references in my custom hashmap. My hashmap algorithm for this array uses the ar.length member as the modulus so if in the get function, the caller sees a non-current array, all of its references will still be good, and still obey the hashing slots, so if it succeeds without mutex to get a non-null value, that's the right one.
My plan is whenever a get fails, the caller then constructs the correct value for the key does a put which does locking relative to other puts and stuffs an object into the array. Just before exiting the critical section the put code reassigns the volatile array field "ar" to itself, hopefully as a message to the compiler and hotspot compiler to create a fence relative to the gets which use the volatile array reference to find a hashed value.
This will work as long as the compiler doesn't nop the "ar = ar" assignment:
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)
}
Upvotes: 2
Views: 256
Reputation: 30235
A volatile write can't be optimized away, so yes you'll get the memory guarantees here. And since reading a value from an array also (at least conceptually) means that you read the volatile variable of the array you should get one guaranteed volatile read either.
So while this should work - if you're using Hotspot the usual way to do this is with sun.misc.Unsafe
- you can look at the concurrent collections in Java5 upwards where that pattern is demonstrated often enough. (And yes we can all look forward to getting arrays of volatile elements in the future - afaik Doug Lea and Co are working on a specification for that, but no idea how far they are.)
Although the question is why you implement this yourself - there's Cliff's non blocking hashmap that has some pretty strong correctness guarantees (afaik they checked it with CHESS for one and lots of people have looked at the underlying state machine) and excellent performance compared to the ConcurrentHashMap from the JDK.
And certainly faster than having a synchronized put
operation.
Upvotes: 2