[concurrency-interest] Adding a createIfAbsent() API?

Rowlands, Ben (IT) Ben.Rowlands at morganstanley.com
Mon Aug 13 13:08:11 EDT 2007


I often find a need for an API like:

  public V createIfAbsent( K key, Callable<V> creator ) throws
ExcecutionException

Similar in operation to putIfAbsent() but taking a callback that is used
to resolve the value if one isn't found at at the key. 

A very common use-case of this is when we are using the Map to cache
values (for example, from a database query). We only want to execute the
database query if the value isn't already in the map so we can't use
putIfAbsent() directly and we end up wrapping the Map using a
lock-per-key or some other technique.

I know this kind of behaviour can be achieved using a
FutureTask/Executor etc (as described in "Concurrency in Practice")
however it seems such a useful primitive that I think it would be useful
to add to the ConcurrentMap API (or a new API in JUC) and would avoid
the copy/paste or need to roll your own implementation each time (add
will be even more succinct with closure support :). A slight difference
in the behaviour of this API is that if the creator fails with an
exception subsequent threads can have a go at creating the value rather
than forcing them to fail with the original exception.

The simple implementation below demonstrates this API using a
lock-per-key (note, createIfAbsent() -> getOrCreate()). 

Are there any plans for this sort of API in JUC? (or is there a 1-2 line
equivalent that I've overlooked and could be used instead?)

Thanks,

Ben Rowlands

----

public interface ConcurrentCreator<K, V>
{
  /**
   * Gets the value mapped to the key invoking the creator if no value
is set.
   * 
   * @param key
   * @param creator produces a value for the given key, will only be
called if there is no value
   *          currently mapped to the key.
   * @return the value mapped to the key, creating it if necessary.
   * @throws ExecutionException if any exceptions are thrown creating
the value.
   */
  public V getOrCreate( K key, Callable<V> creator ) throws
ExecutionException;

  public V get( K key );

  public V put( K key, V value );
}

----

// uses a lock-per-key to queue conflicting creators
public class SimpleConcurrentCreator<K, V> implements
ConcurrentCreator<K, V>
{
  private final ConcurrentMap<K, Lock<V>> map = new ConcurrentHashMap<K,
Lock<V>>();

  public V put( K key, V value )
  {
    Lock<V> newLock = new Lock<V>();
    Lock<V> fromMap = map.putIfAbsent( key, newLock );
    Lock<V> lock = fromMap == null ? newLock : fromMap;

    V oldValue = null;

    synchronized( lock )
    {
      if( lock.hasValue() )
      {
        oldValue = lock.getValue();
      }

      lock.setObject( value );
    }

    return oldValue;
  }

  public V get( K key )
  {
    Lock<V> lock = map.get( key );

    if( lock == null )
    {
      return null;
    }

    synchronized( lock )
    {
      return lock.getValue();
    }
  }

  public V getOrCreate( K key, Callable<V> creator ) throws
ExecutionException
  {
    Lock<V> newLock = new Lock<V>();
    Lock<V> fromMap = map.putIfAbsent( key, newLock );
    Lock<V> lock = fromMap == null ? newLock : fromMap;

    synchronized( lock )
    {
      if( lock.hasValue() )
      {
        return lock.getValue();
      }

      try
      {
        V result = creator.call();
        lock.setObject( result );
        return result;
      }
      catch( Exception e )
      {
        throw new ExecutionException( "Failed to generate value for key:
" + key, e );
      }
    }
  }

  private static final class Lock<V>
  {
    private V _object;

    private boolean _isSet; // allows for null value

    public V getValue()
    {
      return _object;
    }

    public boolean hasValue()
    {
      return _isSet;
    }

    public void setObject( V object_ )
    {
      _isSet = true;
      _object = object_;
    }
  }
}

----
--------------------------------------------------------

NOTICE: If received in error, please destroy and notify sender. Sender does not intend to waive confidentiality or privilege. Use of this email is prohibited when received in error.



More information about the Concurrency-interest mailing list