2012-06-20 9 views
14

¿Cuál es la mejor manera de evitar la actualización simultánea de un registro en un conjunto clave-valor sin bloquear todo el conjunto? Semánticamente, estoy buscando algún tipo de bloqueo por una llave (a ser posible, la aplicación Java, pero no necesariamente):Cómo adquirir un candado con una llave

interface LockByKey { 
    void lock(String key); // acquire an exclusive lock for a key 
    void unlock(String key); // release lock for a key 
} 

Este bloqueo está destinado para sincronizar el acceso a una tienda a distancia, por lo que algunos colección de Java sincronizado no es una opinión.

+1

Esto tiene mucho que ver con la forma de sincronizar el acceso a su tienda remota. No estoy seguro de si esta pregunta se puede responder sin saber más acerca de cómo administrar la concurrencia de forma remota. –

+0

Eche un vistazo a http://stackoverflow.com/a/28347825/704335. – Timmos

+0

Puede usar: http://stackoverflow.com/a/28723518/1183010 –

Respuesta

25

Guava tiene algo así como esto lanzado en 13.0; puedes sacarlo de HEAD si quieres.

Striped<Lock> asigna más o menos un número específico de bloqueos, y luego asigna cadenas a bloqueos basados ​​en su código hash. La API se ve más o menos como

Striped<Lock> locks = Striped.lock(stripes); 
Lock l = locks.get(string); 
l.lock(); 
try { 
    // do stuff 
} finally { 
    l.unlock(); 
} 

Más o menos, el número controlable de rayas le permite operar concurrencia contra el uso de memoria, debido a la asignación de un bloqueo total para cada clave de cadena puede ser caro; esencialmente, solo obtienes contienda de bloqueo cuando obtienes colisiones hash, que son (previsiblemente) raras.

(Revelación:. Contribuyo a guayaba)

+0

¿se limpia la memoria asociada a los bloqueos antiguos? como lo harían si se mantienen en un WeakHashMap. –

+2

Lo hace si usa el método de fábrica para generar bloqueos débiles. –

+1

@Jose Martinez: no, no limpia nada, no tiene que hacerlo, opera en un principio diferente. Tomemos por ejemplo 'Striped.lock (1024)', que crea una matriz de bloqueo simple [1024], inicializada con impaciencia con 1024 objetos de bloqueo pregenerados; ver 'Striped.CompactStriped'. Puede tener una aplicación con miles de millones de ID únicos, pero su grupo de bloqueo permanece en 1024 siempre con los mismos bloqueos. 'Striped' funciona estadísticamente con MUY baja probabilidad de 2, o más, identificadores que generan el mismo hash intentando acceder al mutex al mismo tiempo. – Espinosa

0

Mantenga un mutex/bloqueo por cubo. Esto asegurará que solo las colisiones esperen en ese mutex.

0

Si el "registro" que menciona es un objeto mutable y "actualizar" significa que el estado interno del objeto se modifica sin alterar la estructura del contenedor, entonces puede lograr lo que desee simplemente bloqueando el objeto de registro.

Sin embargo, si "actualizar" significa eliminar el objeto de registro del contenedor y reemplazarlo, entonces debe bloquear todo el contenedor para evitar que otros subprocesos lo vean en un estado incoherente.

En cualquier caso, debe consultar las clases en el paquete java.util.concurrent.

1

Esta es la forma; lo hice. Y sí, estoy de acuerdo en que si dos cadenas diferentes comparten el mismo código hash terminarán adquiriendo el mismo bloqueo.

class LockByKey { 
    ObjectForString objHolder = new ObjectForString(100); 
    public void lockThenWorkForKey (String key) { 
     synchronized(objHolder.valueOf(key)){ 
      //DoSomeWork 
     } 
    } 
} 

public final class ObjectForString { 

    private final Object[] cache; 
    private final int cacheSize; 
    final int mask; 

    public ObjectForString(int size) { 
     // Find power-of-two sizes best matching arguments 
     int ssize = 1; 
     while (ssize < size) { 
      ssize <<= 1; 
     } 

     mask = ssize - 1; 
     cache = new Object[ssize]; 
     cacheSize = ssize; 
     //build the Cache 
     for (int i = 0; i < cacheSize; i++) { 
      this.cache[i] = new Object(); 
     } 
    } 

    public Object valueOf(String key) { 
     int index = key.hashCode(); 
     return cache[index & mask]; 
    } 
} 
Cuestiones relacionadas