If I simply need to guarantee `happens-before` relation, and not atomicity, should I use a concurrent class or a synchronized block?

StackOverflow https://stackoverflow.com/questions/19340353

Question

I am developing a pool mechanism. Objects in the pool will be used by different threads. Thus I need to guarantee that there will be a happens-before relation when accessing non-final attributes of these objects.

As a given object will be accessed by only one thread at a time, I do not need to guarantee any atomicity. What is more optimized when only happens-before needs to be achieved? To use a synchronized block on a final attribute each time I read or modify one of the non-final attribute? Or to use concurrent classes, such as a ConcurrentMap?

(I am not asking this question for attributes of the pool itself, that obviously needs to provide atomicity; I am only asking this question for the attributes of objects obtained from the pool)

Was it helpful?

Solution

To use a synchronized block on a final attribute each time I read or modify one of the non-final attribute? Or to use concurrent classes, such as a ConcurrentMap?

Synchronization is built around an internal entity known as the intrinsic lock or monitor lock. Every object has an intrinsic lock associated with it. By convention, a thread that needs exclusive and consistent access to an object's fields has to acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them.

An implementation of ConcurrentMap i.e., ConcurrentHashMap uses reentrant lock which is mutual exclusive lock, Lock is acquired by lock() method and held by Thread until a call to unlock() method. Though, ReentrantLock provides same visibility and orderings guaranteed as implicit lock, acquired by synchronized keyword, it provides more functionality and differ in certain aspect:

  1. ReentrantLock can be made fair by specifying fairness property to provides lock to longest waiting thread, in case of contention.
  2. provides convenient tryLock() method to acquires lock, only if its available or not held by any other thread, reducing blocking of thread waiting for lock. tryLock() with timeout can be used to timeout if lock is not available in certain time period.
  3. In case of synchronized keyword, a thread can be blocked waiting for lock, for an indefinite period of time and there was no way to control that. ReentrantLock provides a method called lockInterruptibly(), which can be used to interrupt thread when it is waiting for lock.

An example of use reentrant lock can be shown from ConcurrentHashMap's inner replace(K key, V oldValue, V newValue) function implementaion:

boolean replace(K key, int hash, V oldValue, V newValue) { 
               // called by replace(K key, V oldValue, V newValue)
        lock(); // acquire the lock
        try {
            HashEntry<K,V> e = getFirst(hash);
            while (e != null && (e.hash != hash || !key.equals(e.key)))
                e = e.next;

            boolean replaced = false;
            if (e != null && oldValue.equals(e.value)) {
                replaced = true;
                e.value = newValue;
            }
            return replaced;
        } finally {
            unlock(); // unlock 
        }
    }

There are other functions like put(), writeObject(java.io.ObjectOutputStream), etc. are also implemented using reentrant synchronization using the ReentrantLock, without which, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block. That is why I think for your case ConcurentMap is preferable.

Reference:

  1. Intrinsic Locks and Synchronization
  2. ReentrantLock Class
  3. Difference between synchronized vs ReentrantLock
  4. ConcurrentHashMap

OTHER TIPS

As a given object will be accessed by only one thread at a time, I do not need to guarantee any atomicity.

If I get it right, what you mean is that your objects will be shared accross threads, but there is actually no concurrency going on (objects are never accessed concurrently by multiple threads). You're wondering what synchronization is needed.

I've been wondering the same, and my conclusion was the following: if object Y synchronizes accesses to object X, object X doesn't need to be synchronized itself. In the case of a pool, you probably obtain and release objects using methods that are synchronized, so they protect the object in the pool.

Let's consider object O in the pool. The pool has two methods Object acquire() and release(Object o) that are synchronized. To pass object O from thread T1 to T2, T1 must first release O, and T2 must then acquire O. There is already a happen-before relationship between T1's release and T2's acquire, so the object O doesn't need to be itself synchronized.

If the field you need the happens-before relationship on is a primitive, and you are using Java 5 or later, then you can use the volatile keyword which in Java 5+ ensures happens-before reading.

If it's an object, then you need to use some kind of synchronization either using the synchronized key word or a class from the java.util.concurrent package which often has better performance than naive synchronization. For example, ConcurrentHashMap usually has better performance than Collections.synchronizedMap(Map) because it uses separate locks for each bin so there is less lock contention.

well if you need to guarantee a Happens-Before, i think you can create a Synchronized block, holding the Object you want to lock, then in this block, do what you want it to be done before using this object, then invoke your stuff from the Object, then release

for instance

public void doSomething(){
    synchronized(myObject){
        doAnotherThing();
        myObject.doStuff();
    }
}

i didnt try the ConcurrentMap before by the way ... and by the way, please clarify your example because i think i didnt get you right !!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top