3

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 {

    }
Kedar Mhaswade
  • 4,535
  • 2
  • 25
  • 34
Crabonog
  • 423
  • 3
  • 12
  • 1
    Actually, you are talking about some kind of Map with tricky behavior in multi-threading (somehow such behavior seem to be similar to some kind of Queue logic). As far as I know there is no such behavior out of the box in any Java implementation of collections. In any case, you have to modify library implementation. – sphinks Mar 25 '16 at 15:46
  • Are you talking about BlockingQueue? – Yaroslav Rudykh Mar 25 '16 at 15:47
  • @YaroslavRudykh I suppose it is very close, but no Key-Object relation. In Queue we have read object by order, not by key. – sphinks Mar 25 '16 at 15:51
  • @YaroslavRudykh I talking about something sort of BlockingQueue. BlockingQueue is used for Producer-Consumer pattern, but I talk about another things. When we use blocking-queue we just signal like that "hey, there is a new element in this container." and everybody can get it. But I need the pattern that will give specific object for specific waiter. When we call get(Key key) method, we want object exaclty associated with the key. Not any object that will be added to this container, like this happens in blocking-queue. – Crabonog Mar 25 '16 at 15:51
  • Ok. For me now it's called "Blocking Concurrent Map" – Crabonog Mar 26 '16 at 11:12
  • Do you also need the writer thread to wait until the element is read by the other thread? – fps Mar 27 '16 at 20:17
  • Just added sample code to [my answer](http://stackoverflow.com/a/36251307/823393) – OldCurmudgeon Apr 04 '16 at 14:09

2 Answers2

2

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));
    }
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
1

Looking at your design, to me what you are describing

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. This container should work in multi-threading environment

is close to concurrent Object pool. It uses a set of initialized objects kept ready to use. A client of the pool will request an object from the pool and perform operations on the returned object.

The only real difference I see is that you are getting the objects based on your own criteria.

ekostadinov
  • 6,880
  • 3
  • 29
  • 47