/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.toolkit.modules.concurrency.utils;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* A thread-safe map that creates requested entries on demand if they don't exist yet.
*
* A typical use case are counter maps, where for each id, an atomic integer or long holder is
* created when the key is used for the first time. Subsequent calls to {@link #get(Object)} return
* this holder, which can then be incremented or queried for the counter value.
*
* @param <K> the key type
* @param <V> the value type
*
* @author Robert Mischke
*/
public abstract class ThreadsafeAutoCreationMap<K, V> {
// TODO reduce lock contention?
private final Map<K, V> innerMap = Collections.synchronizedMap(new HashMap<K, V>());
/**
* Similar to the standard {@link Map#get(Object)}, but with the addition that if no entry
* exists yet, one is created by the subclass implementation of {@link #createNewEntry(Object)}.
*
* @param key the map key
* @return the retrieved value
*/
public V get(K key) {
V value = innerMap.get(key);
if (value == null) {
// NOTE: while this looks similar to the double-checked locking anti-pattern,
// it should be safe as statisticsMap is a synchronizedMap; the synchronized block only
// serves to prevent race conditions <b>between</b> the already-synchronized calls
synchronized (innerMap) {
value = innerMap.get(key);
if (value == null) {
value = createNewEntry(key);
innerMap.put(key, value);
}
}
}
return value;
}
/**
* @see Map#remove(Object)
*
* @param key the entry to delete
*/
public void remove(String key) {
synchronized (innerMap) {
innerMap.remove(key);
}
}
/**
* @see Map#clear()
*/
public void clear() {
// note: explicit synchronization might be redundant here
synchronized (innerMap) {
innerMap.clear();
}
}
/**
* Creates a shallow copy of the internal, synchronized map. Note that the entries themselves
* are not synchronized or cloned in any form, so they may be subject to concurrent changes from
* calls to the producing {@link ThreadsafeAutoCreationMap}.
*
* @return a detached map with all key-value pairs
*/
public Map<K, V> getShallowCopy() {
synchronized (innerMap) {
return new HashMap<K, V>(innerMap);
}
}
protected abstract V createNewEntry(K key);
}