2013-04-22 8 views
13

Secondo Java Concurrency in Practice, capitolo 11.4.3 dice:Desideri semplice spiegazione di come "blocco striping" lavora con ConcurrentHashMap

Blocco scissione a volte può essere esteso a partizionare di chiusura su una serie di oggetti indipendenti variablesized , nel qual caso si chiama blocco a strisce. Ad esempio, l'implementazione di ConcurrentHashMap utilizza una matrice di 16 blocchi, ciascuno dei quali protegge 1/16 dei bucket hash; il secchio N è protetto da una serratura N mod 16.

Ho ancora problemi a capire e visualizzare il meccanismo di blocco della striscia e dei secchi. Qualcuno può spiegare questo con parole di buona comprensione :)

Grazie in anticipo.

risposta

30

La mappa di hash è costruita su una matrice, in cui la funzione di hash associa un oggetto a un elemento nell'array sottostante. Diciamo che l'array sottostante ha 1024 elementi - ConcurrentHashMap trasforma effettivamente questo in 16 diversi sotto-array di 64 elementi, ad es. {0, 63}, {64, 127}, ecc. Ogni sub-array ha il proprio lock, quindi la modifica del sub-array {0, 63} non ha alcun impatto sul sub-array {64, 127} - un thread può scrivere sul primo sub-array mentre un altro thread scrive sul secondo sub-array.

+0

Ok, ho capito. Ma cosa succede se due o più thread stanno cercando di modificare tutto nel sotto-array {0,63}? – GedankenNebel

+3

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. –

+0

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

11

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(); 
} 
+0

HashMap in java non è thread-safe, HashTable è. Il primo scenario di cui parli è per HashTable –

2

Il concetto chiave qui è il "secchio". utilizzando invece un blocco globale per questa intera tabella hash, utilizza un piccolo blocco per ciascun bucket. È anche un buon analogo al tipo di benna che può migliorare la complessità di ordinamento.