2011-11-05 9 views
14

Le tabelle di sicurezza del thread di ritorno del metodo Tables.newCustomTable(Map, Supplier) di Guava verranno fornite con le mappe thread-safe? Ad esempio:Un thread di Guava Table è sicuro quando le sue mappe di supporto sono thread-safe?

public static <R, C, V> Table<R, C, V> newConcurrentTable() { 
    return Tables.newCustomTable(
     new ConcurrentHashMap<R, Map<C, V>>(), 
     new Supplier<Map<C, V>>() { 
     public Map<C, V> get() { 
      return new ConcurrentHashMap<C, V>(); 
     } 
     }); 
} 

Il codice restituisce effettivamente tabelle concorrenti?

+0

Qual è la vostra definizione di "tabelle concorrenti?" –

+0

Buona domanda. Per esprimere la mia domanda in un altro modo: quei tavoli esploderanno in modi che una ConcurrentMap > non lo farebbe? E per "esplodere" intendo entrare in loop infiniti, lanciare eccezioni, o fare qualsiasi altra cosa che farebbe un normale HashBasedTable se provassi a leggere e scrivere su più thread simultaneamente. –

risposta

19

Dal doc: "Se più thread accedono contemporaneamente a questa tabella e uno dei thread modifica la tabella, deve essere sincronizzato esternamente."

Le raccolte di supporto simultanee non sono sufficienti.

+0

Stranamente, quella frase in javadoc è ciò che mi ha spinto a fare questa domanda. Oh bene. Grazie, Kevin. –

16

Kevin Bourrillion ha ragione. Il motivo tecnico per la mappa che hai costruito per non essere thread-safe è che anche se le mappe che stai utilizzando sono thread-safe, le operazioni da tabella potrebbero non esserlo. Vi faccio un esempio di put, come attuato nel StandardTable, che viene utilizzato da Tables.newCustomTable:

public V put(R rowKey, C columnKey, V value) { 
    Map<C, V> map = backingMap.get(rowKey); 
    if (map == null) { 
    map = factory.get(); 
    backingMap.put(rowKey, map); 
    } 
    return map.put(columnKey, value); 
} 

sicurezza Discussione è compromesso nella gestione del caso map == null. Vale a dire, due o più thread potevano entrare in quel blocco e creare una nuova voce per lo columnKey e l'ultimo per eseguire uno backingMap.put(rowKey, map) sostituiva la voce per lo columnKey nello backingMap, che porterebbe alla perdita delle operazioni di put eseguite da altri filettature. In particolare, il risultato di questa operazione in un ambiente con multithreading non è deterministico, il che equivale a dire che questa operazione non è thread-safe.

La corretta applicazione di questo metodo potrebbe essere:

public V put(R rowKey, C columnKey, V value) { 
    ConcurrentMap<C, V> map = table.get(rowKey); 
    if (map == null) { 
     backingMap.putIfAbsent(rowKey, factory.get()); 
    } 
    map = backingMap.get(rowKey); 
    return map.put(columnKey, value); 
} 

Attualmente sto studiando se è possibile utilizzare l'implementazione ForwardingTable insieme a quello che hai voluto fare, per ottenere un corretto thread safe ConcurrentTable.

Ma ad essere onesti, penso che il motivo non v'è alcuna implementazione thread-safe del Table è che l'interfaccia stessa non fornisce alcuna costrutti di concorrenza, come ad esempio putIfAbsent o replace.

+1

Dopo aver esplorato a fondo l'implementazione di 'StandardTable', sono giunto alla conclusione che per garantire la sicurezza dei thread ci sono solo due possibilità: creare un'implementazione da zero o fornire un wrapper che blocca ogni accesso. Il motivo è che le viste restituite da row(), column() o columnMap() non sono thread-safe e non c'è modo di modificare il loro comportamento attraverso l'override del metodo. – velocipedist

Problemi correlati