2011-11-05 15 views
14

¿El método Tables.newCustomTable(Map, Supplier) de Guava devolverá las tablas de seguridad de subprocesos cuando se suministre con mapas de seguridad de subprocesos? Por ejemplo:¿Es seguro un hilo de tabla Guava cuando sus mapas de respaldo son seguros para subprocesos?

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

¿Ese código realmente devuelve tablas concurrentes?

+0

¿Cuál es su definición de "tablas simultáneas"? –

+0

Buena pregunta. Para expresar mi pregunta de otra manera: ¿Las tablas explotarán de forma tal que ConcurrentMap > no lo haga? Y por "explosión" me refiero a entrar en bucles infinitos, lanzar excepciones o hacer cualquier cosa que haga una HashBasedTable habitual si intenta leer y escribir en múltiples hilos simultáneamente. –

Respuesta

19

Desde el documento: "Si varios subprocesos acceden a esta tabla al mismo tiempo y uno de los subprocesos modifica la tabla, se debe sincronizar externamente".

Las colecciones simultáneas de respaldo no son suficientes.

+0

Por extraño que parezca, esa frase en el javadoc es lo que me impulsó a hacer esta pregunta. Oh bien. Gracias, Kevin. –

16

Kevin Bourrillion tiene razón. La razón técnica para que el mapa que ha construido no sea seguro para subprocesos es que incluso si los mapas que está utilizando son seguros para subprocesos, las operaciones de tabla pueden no serlo. Déjenme darles un ejemplo de puesta, como se aplica en la StandardTable, que es utilizado por 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); 
} 

Seguridad de los hilos se ve comprometida en el manejo del caso map == null. A saber, dos o más hilos podrían entrar en ese bloque y crear una nueva entrada para el columnKey y el último para realizar un backingMap.put(rowKey, map) anularía finalmente la entrada para el columnKey en el backingMap, lo que llevaría a la pérdida de put operaciones realizadas por otro trapos. En particular, el resultado de esta operación en un entorno multiproceso no es determinista, lo que equivale a decir que esta operación no es segura para subprocesos.

la correcta aplicación de este método sería:

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); 
} 

Actualmente estoy investigando si es posible utilizar la aplicación ForwardingTable junto con lo que has querido hacer, para conseguir una adecuada ejecución de subprocesos ConcurrentTable.

Pero para ser honesto, creo que la razón no hay ninguna aplicación segura para los subprocesos de la Table es que la interfaz no proporciona ningún construcciones de concurrencia, como putIfAbsent o replace.

+1

Después de haber examinado exhaustivamente la implementación de 'StandardTable', llegué a la conclusión de que, para proporcionar seguridad de subprocesos, solo tiene dos opciones: crear una implementación desde cero o proporcionar un contenedor que bloquee cada acceso. El motivo es que las vistas devueltas por row(), column() o columnMap() no son seguras para subprocesos y no hay forma de modificar su comportamiento mediante anulaciones de métodos. – velocipedist

Cuestiones relacionadas