[concurrency-interest] Extending with an exposed lock?

Doug Lea dl at cs.oswego.edu
Sun Oct 8 09:33:10 EDT 2006


[First: Sorry that a misconfigured firewall elsewhere on our campus
was interfering with DNS, so some of you might not have gotten the
original or had a reply stall. This should now be fixed.]

Moran Avigdor wrote:
> Extending a concurrent construct (for example CyclicBarrier, 
> ThreadPoolExecutor, etc.) may impose limitations
> especially when it regards to internal locking mechanisms used by the 
> 'super' class.
> 
> My specific extended use has no additional value to this post, thus I 
> will refrain from elaborating on it - unless interest arises.
> 
> The concurrency difficulty here is that my extended code needs to be 
> atomic in respect to other code conducted
> through the same API. If I choose an 'outer' locking it may lead to a 
> deadlock if 'inner' (super's) locking is in await states
> waiting for a condition to settle.
> 
> Exposing the lock (via getter) would impose ambiguities between 
> different lock implementations (more specifically backport vs. 
> java.util.concurrent package) - not to mention misuse.
> 
> The option that I am raising is having a /protected V 
> underLock(Callable<V> c) /that will be used to delegate logic from the
> extending class to be performed under the 'inner' lock used by the super 
> implementation.
>  >From the extending class point of view, the 'inner' lock can be of any 
> kind, including a synchronization of an Object used by backport.
> 

 > The ThreadPoolExecutor has in this regard an afterExecute(Runnable,
 > Throwable) and beforeExecute(Thread, Runnable) calls.
 > Those have been used successfully.
 >
 > But, does anyone see another option under the current limitations
 > (besides reflection to get the inner lock...)?
 >


There are several different kinds of cases here:

1. Components that do no internal blocking. In these, you can, if
desired, wrap all usages in an outer lock. This will probably
decrease throughput, but leaves you in control. This applies
for example to ConcurrentSkipLists, which don't even use locks
internally, so support a lot of internal concurrency. Using
an external before/after-style lock would disable internal
concurrency, but with no other effects on liveness.

2. Components like Semaphore, CountDownLatch, SynchronousQueue,
and Exchanger that may do internal blocking, but without using locks.
For these, you'd never want to "extend" the atomicity of methods even
with before/after locks, since a thread may block while holding
lock, preventing others from calling methods that would unblock
it (this is an analog of nested monitor lockout). In these cases,
the only kind of external before/after extensions you can use
with blocking methods are those that both acquire and release
external lock both before and after. As in:

class MySemaphore extends Semaphore {
   private synchronized void before() { ... stuff ...}
   private synchronized void after() { ... other stuff ... }
   public void acquire() throw InterruptedException {
     before(); try { super.acquire(); } finally { after(); }
   }
}

This might seem overly restrictive, but I don't know of use
cases where you'd want to do anything beyond this.

3. Components that are like (2) but just so happen to use
Lock/Condition for waiting. At the moment, this includes
CyclicBarrier, ArrayBlockingQueue, PriorityBlockingQueue.
But all of these might at any time change to instead use
strategies in (2), which are usually more efficient and
desirable on other grounds, so we really don't want to
expose locks in these cases and preclude further improvements.
So, in these cases, you also can only use the above style.
(There are also in-between cases like current LinkedBlockingQueue,
that use locks and conditions, but still permit some internal
concurrency.)


Can you live with this?

-Doug


More information about the Concurrency-interest mailing list