package edu.ucsb.jpregel.system; import java.util.concurrent.ConcurrentMap; import org.infinispan.util.concurrent.jdk8backported.ConcurrentHashMapV8; /** * A map that creates objects on demand as they are requested. Uses Longs as * keys, and does not create value objects unless their keys do not already have * a corresponding value in the map. * * If multiple threads get(Long key) the same key, one thread will create a * value object and the others will block until the object is created, and then * they will get a reference to it. Because this class is final, this is nothing * to worry about. * * @author Charles Munger */ final public class OntoMap<V> { private static final ThreadLocal< Holder> uniqueNum = new ThreadLocal<Holder>() { @Override protected Holder initialValue() { return new Holder(); } }; private final Factory<V> factory; private final ConcurrentMap<Long, Holder<V>> map; public OntoMap(Factory<V> factory) { this(100, factory); } public OntoMap( int size, Factory<V> factory ) { map = new ConcurrentHashMapV8<Long, Holder<V>>( 100, 0.9f, 2 ); this.factory = factory; } public V get( final Long key ) { final Holder<V> h = uniqueNum.get(); final Holder<V> putIfAbsent = map.putIfAbsent(key, h); if (putIfAbsent != null) { return putIfAbsent.blockingGet(); } else { final V make = factory.make(); h.fill(make); uniqueNum.set(new Holder()); return make; } } public V remove(Long key) { final Holder<V> remove = map.remove(key); return remove == null ? null : remove.get(); } private static class Holder<T> { private volatile T contents = null; void fill(T contents) { this.contents = contents; synchronized (this) { notifyAll(); } } T blockingGet() { if ( contents == null ) { synchronized (this) { try { while ( contents == null ) { wait(); } } catch ( InterruptedException ex ) { System.out.println( "Wait interrupted in get" ); } } } return contents; } T get() { return contents; } } }