javamultithreadingdesign-patternscontainersreaderwriterlock

How is this "container Design Pattern" called?


While creating my app. architecture I faced the need for one structure, that will be described below.

I'm pretty sure, that there is a well known design pattern with the same functionality, because I think that problem, for which I develop it is really common.

I write my own implementation of this, but I always try to use "build in language" implementations of patterns, so - please help me to name this construction.

The idea is close to reader-writer pattern. We have a "container" in which we can add Objects by the key (). And also we can get this objects by keys, removing it from container.

So, the implemented class should have two methods:

void putObject(Key key, Object object);
Object getObject(Key key); // remove <Key,Object> from container.

The next is most interesting. This container should work in multi-threading environment as follows:

  1. If there is no object associated with key, while calling get(Key key) method the caller thread should WAIT for the object in this container.
  2. When another thread will call putObject(Key key, Object object) method it should check if there is some thread that wait exactly for this object, and if it is - then signal and wake up the thread that waits.

I think that it is common structure, does it have "official" name?

My Java implementation of this pattern:

private static interface BlackBox {

        public void addObject(IdObject object);

        public IdObject getObject(ObjectId id);

    }

    private static class BlackBoxImpl implements BlackBox {

        private final Lock conditionLock = new ReentrantLock();
        private final Map<ObjectId, IdObject> savedObjects;
        private final Map<ObjectId, Condition> waitingConditions;

        public BlackBoxImpl() {
            this.savedObjects = new ConcurrentHashMap<ObjectId, IdObject>(20);
            this.waitingConditions = new ConcurrentHashMap<ObjectId, Condition>(20);
        }

        @Override
        public void addObject(IdObject object) {
            savedObjects.put(object.getId(), object);
            if (waitingConditions.containsKey(object.getId())) {
                Condition waitCondition = waitingConditions.get(object.getId());
                conditionLock.lock();
                waitCondition.signal();
                conditionLock.unlock();
            }
        }

        @Override
        public IdObject getObject(ObjectId id) {
            if (savedObjects.containsKey(id)) {
                return savedObjects.get(id);
            } else {
                conditionLock.lock();
                Condition waitCondition = conditionLock.newCondition();
                waitingConditions.put(id, waitCondition);
                waitCondition.awaitUninterruptibly();
                conditionLock.unlock();
                return savedObjects.get(id);
            }
        }

    }

    private static interface IdObject {

        public ObjectId getId();

    }

    private static class IdObjectImpl implements IdObject {

        protected final ObjectId id;

        public IdObjectImpl(ObjectId id) {
            this.id = id;
        }

        @Override
        public ObjectId getId() {
            return id;
        }

    }

    private static interface ObjectId {

    }

    private static class ObjectIdImpl implements ObjectId {

    }

Solution

  • I would probably use something like a

    ConcurrentMap<K,BlockingQue<V>>. 
    

    Use the concurrent methods of the Map to add the pair. Take from your queue for the value. Use an ArrayBlockingQue(1).

    Something like this perhaps:

    static class MultiQueue<K, V> {
    
        // The base structure.
        final ConcurrentMap<K, BlockingQueue<V>> queues = new ConcurrentHashMap<>();
    
        /**
         * Put an item in the structure.
         *
         * The entry in the map will be created if no entry is currently there.
         *
         * The value will then be posted to the queue.
         */
        public void put(K k, V v) throws InterruptedException {
            // Make it if not present.
            ensurePresence(k).put(v);
        }
    
        /**
         * Get an item from the structure.
         *
         * The entry in the map will be created if no entry is currently there.
         *
         * The value will then be taken from the queue.
         */
        public void get(K k) throws InterruptedException {
            // Make it if not present - and wait for it.
            ensurePresence(k).take();
        }
    
        private BlockingQueue<V> ensurePresence(K k) {
            // Make it if not present.
            return queues.computeIfAbsent(k, v -> new ArrayBlockingQueue(1));
        }
    }