32

I'd like to have one BlockingMap data structure which is very similar to BlockingQueue. The take method of BlockingQueue will wait there until element is available. I'd like the get method of BlockingMap to wait there until the corresponding key is available? Is this kind of data structure available that I can use ?

zjffdu
  • 25,496
  • 45
  • 109
  • 159
  • 2
    You can always combine ConcurrentHashMap with BlockingQueue, Future or Exchanger to achieve similar result. – Mikhail Jan 06 '14 at 09:05
  • @zjffdu interesting read on `BlockingMap` http://cs.oswego.edu/pipermail/concurrency-interest/2007-June/004211.html – Ajay George Jan 09 '14 at 19:44

3 Answers3

15

I have simply used BlockingQueue<Map.Entry<K,V>> in the past. But recently, I came across this Blocking Map for Java. Haven't used it myself, though.

Chthonic Project
  • 8,216
  • 1
  • 43
  • 92
12

Here is an extremely simple implementation using BlockingQueue and ConcurrentHashMap:

public class BlockingMap<K, V> {
    private Map<K, ArrayBlockingQueue<V>> map = new ConcurrentHashMap<>();

    private BlockingQueue<V> getQueue(K key) {
        return map.computeIfAbsent(key, k -> new ArrayBlockingQueue<>(1));
    }

    public void put(K key, V value) {
        // can also use add(value) if you want an exception thrown
        if ( !getQueue(key).offer(value) ) {
            System.err.println("Ignoring duplicate key");
        }
    }

    public V get(K key) throws InterruptedException {
        return getQueue(key).take();
    }

    public V get(K key, long timeout, TimeUnit unit) throws InterruptedException {
        return getQueue(key).poll(timeout, unit);
    }
}
Ofri Mann
  • 361
  • 3
  • 7
  • 1
    Your `getQueue()` method could be simplified by using `putIfAbsent` and `getOrDefault`, respectively – Stefan Haberl Jan 27 '21 at 09:13
  • I don't see how this class could work. If a thread does a get(), a queue is created and is waited on. Then, if another thread does put() over the same key, a new queue is created. But the previous queue never gets added to. So, the thread waiting on the first queue waits forever. Am I missing anything? – elmart Jan 12 '22 at 16:11
  • @elmart an invocation of `getQueue` with the second argument as `true` should indeed create a new queue for the same key, as far as I too understand. So `BlockingMap#put` will always create a new queue, which seems to be actually *not* desired, because it will cause the effect you are describing. Maybe if the second argument to `getQueue` is always `false` (or entirely dropped) then it should work. – gthanop Feb 26 '22 at 14:26
  • Good point @elmart - I edited the code accordingly – Ofri Mann Apr 18 '22 at 13:10
4

I hope this is what you want.

public class BlockingHashMap<K,V>
extends java.lang.Object
implements BlockingMap<K,V>

get

public V get(java.lang.Object key)

Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. Note that null is used as a special marker to indicate the absence of the requested key

Specified by:

get in interface java.util.Map<K,V>

Specified by:

get in interface BlockingMap<K,V>

Parameters:

key - the key whose associated value is to be returned

Returns:

the value to which the specified key is mapped, or null if this map contains no mapping for the key

Throws:

java.lang.ClassCastException - if the key is of an inappropriate type for this map
java.lang.NullPointerException - if the specified key is null and this map does not permit null keys (optional)
java.lang.IllegalStateException - if the map has been shut-down
Reuben
  • 5,556
  • 10
  • 43
  • 55