La differenza tra bloccaggio in una Collections.synchronizedMap()
e ConcurrentHashMap
è la seguente:
Se più thread accedono un Collections.synchronizedMap()
frequentemente, ci saranno un sacco di contesa poiché ogni metodo è sincronizzato con un blocco condiviso (cioè se thread X chiama un metodo su un Collections.synchronizedMap()
, tutti gli altri thread verranno bloccati dal chiamare qualsiasi metodo su un Collections.synchronizedMap()
finché il thread X non torna dal metodo chiamato).
A ConcurrentHashMap
ha un numero variabile di blocchi (il valore predefinito è 16) che ciascuno protegge un segmento delle chiavi nello ConcurrentHashMap
. Quindi per uno ConcurrentHashMap
con 160 chiavi, ogni blocco proteggerà 10 elementi. Pertanto, i metodi che funzionano su un tasto (get
, put
, set
, ecc.) Bloccano l'accesso ad altri metodi operativi su una chiave in cui le chiavi si trovano nello stesso segmento. Ad esempio, se il thread X chiama put(0, someObject)
e quindi chiama il thread Y put(10, someOtherObject)
, tali chiamate possono essere eseguite contemporaneamente e il thread Y non deve attendere il thread X per tornare da put(0, someObject)
. Un esempio è fornito di seguito.
Inoltre, alcuni metodi come size()
e isEmpty()
non sono affatto protetti. Sebbene ciò consenta una maggiore concorrenza, significa che non sono fortemente coerenti (non riflettono lo stato che sta cambiando contemporaneamente).
public static void main(String[] args) {
ConcurrentHashMap<Integer, Object> map = new ConcurrentHashMap<>(160);
new Thread(new Runnable() {
@Override
public void run() {
map.put(0, "guarded by one lock");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
map.put(10, "guarded by another lock");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
// could print 0, 1, or 2
System.out.println(map.count());
}
}.start();
}
fonte
2013-04-22 16:22:58
Ok, ho capito. Ma cosa succede se due o più thread stanno cercando di modificare tutto nel sotto-array {0,63}? – GedankenNebel
Quindi viene innanzitutto il primo servito - il primo thread per acquisire il blocco apporta le sue modifiche, quindi quando finisce il secondo thread apporta le sue modifiche. 'ConcurrentHashMap' ha metodi come' replace' per garantire che il secondo thread non sovrascriva inavvertitamente le modifiche del primo thread. –
Non penso che sia in realtà "primo arrivato, primo servito", come ho capito (non ho la citazione esatta, ma l'ho imparato da Java Concurrency in Practice), l'equità è garantita solo quando è esplicita, come nei costruttori per le diverse implementazioni esplicite di 'Lock', come' ReentrantLock', o Queue come 'ArrayBlockingQueue'. (So che è un vecchio thread, scusate) – Marcelo