A cura di Flavio Casadei Della Chiesa
Leggi l'articolo completo su flaviocdc.net
In questo schema si provvede ed inserire un elemento (di solito) in una collezione solo se questa già non lo contiene.
// schema generico di put-if-absent private HashMap<K,V> hm ..... public void putIfAbsent(K k , V v) { if (!hm.containsKey(k)) { hm.put(k,v); } }i problemi sono i soliti derivanti dal CheckThenAct. Nota, come in tutte le azioni composte non basta limitarsi ad aggiungere un synchronized per risolvere il problema.
Per eliminare ogni sorta di problema è necessario rendere atomica l'operazione. Per far questo solo un Thread deve essere in grado di controllare la presenza o meno della chiave nella mappa e di effettuare un'eventuale sostituzione.
Un esempio di soluzione può essere il seguente nel quale si istanzia una HashMap all'interno di una classe contenitrice
public class InnerPIA<K,V> { private final HashMap<K,V> delegate = new HashMap<K,V>(); public synchronized V get(K k) {return delegate.get(k);} public synchronized void put(K k, V v) { delegate.put(k,v);} public synchronized boolean containsKey(K k) {return delegate.containsKey(k);} public synchronized void putIfAbsent(K k, V v) { if (!delegate.containsKey(k) { delegate.put(k,v); } } ..... }Nel caso in cui la HashMap sia condivisa e non
confinata
in una classe contenitrice è necessario utilizzare un altro tipo di ClientSideLocking. In questo modo però tutte le classi che condividono la solita istanza della mappa devono utilizzare il solito protocollo.
public class DelegatePIAPIA<K,V> { private final Map<K,V> delegate ; public DelegatePIA(Map<K,V> m) { this.delegate = m; } public V get(K k) { synchronized(delegate) { return delegate.get(k); } } public void put(K k, V v) { synchronized(delegate) { delegate.put(k,v); } } public boolean containsKey(K k) { synchronized(delegate) { return delegate.containsKey(k); } } public void putIfAbsent(K k, V v) { synchronized(delegate) { if (!delegate.containsKey(k) { delegate.put(k,v); } } } ..... }In Java5, precisamente nella concurrent API, esiste un modo più immediato: utilizzare una implemantazione di una ConcurrentMap come ad esempio la classe ConcurrentHashMap.
Queste dispongono di un metodo chiamato appunto
putIfAbsent
la cui firma è simile alla precedente; l'unica differenza è che il metodo non è void ma ritorna n elemento di tipo V.
Invocare il metodo
pippo = mappa.putIfAbsent(key,value);è equivalente a
if (!mappa.containsKey(key)) return mappa.put(key, value); else return mappa.get(key);Se la mappa conteneva un valore precedente per la chiave
key
il metodo ritorna tale valore, altrimenti restituisce null
.
Nessun commento:
Posta un commento