2010-11-09 5 views
6

Así que aquí está el problema que estoy tratando de resolver - Tengo un objeto con dos campos de enteros que quiero para almacenar en cachéEhcache - usando una lista <Integer> como el valor de caché

public class MyObject { 
    int x; 
    int y; 
    .... 
} 

Ahora el campo x es lo Principalmente coincido, pero puede haber duplicados, en cuyo caso quiero recurrir al segundo campo (para que this.x = that.x y this.y = that.y). y solo puede ser 25 valores distintos. Ahora sé que podría combinar los dos como un String y usar eso como la clave de caché, pero luego tendría que probar x+[25 possible values] para determinar realmente si no estaba en el caché, lo que hace que el caché sea muy caro. Estaba pensando en intentar almacenar un List<Integer> como el valor de caché para el campo x y luego, si su número era más de uno, recorrer la lista y buscar coincidencias en y.

Ahora si uso un ConcurrentList (o un conjunto si me importan los duplicados, ignóralo por el momento) ¿se podrán agregar varios subprocesos y luego volver a ponerlo en el caché sin condiciones de carrera? ¿Es posible que Ehcache pueda devolver dos Objetos de Lista diferentes a dos subprocesos y luego cuando agreguen su nuevo valor a la lista e intenten volver a ponerlo en la memoria caché, podría obtener resultados indeterminados? ¿Ves una mejor forma de construir este caché?

EDIT: Agradezco las respuestas a continuación, pero parece que a todos les falta el punto principal. esto funcionara? ¿Podría Ehcache realmente devolver dos objetos diferentes para la misma clave de caché (digamos si el objeto estaba en el disco durante la llamada y se serializó dos veces, una para cada llamada).

+0

¿Hay algún motivo por el que no pueda usar un hash como clave? – Falmarri

+0

Como dije en la pregunta que significaría para cualquier valor dado de x, tendría que verificar el caché posiblemente 25 veces para saber realmente que no estaba en el caché. Quiero unirme a x sin importar el valor de y - pero si hay varias x, entonces el mejor valor de y. – Gandalf

+0

Sí, es posible agregar un valor dos veces utilizando el método que mencionaste. Más bien use un ConcurrentMap. –

Respuesta

4

Es absolutamente posible que se obtienen dos instancias diferentes de su lista (o de cualquier Serializable)! Prueba esto:

public static void main(final String[] args) throws Exception { 
    final Cache cache = CacheManager.getInstance().getCache("smallCache"); 

    final List<String> list = new ArrayList<String>(); 
    cache.put(new Element("A", list)); 

    /* We put in a second element. Since maxElementsInMemory="1", this means 
    * that "A" will be evicted from memory and written to disk. */ 
    cache.put(new Element("B", new ArrayList<String>())); 
    Thread.sleep(2000); // We need to wait a bit, until "A" is evicted. 

    /* Imagine, the following happens in Thread 1: */ 
     final List<String> retrievedList1 = 
        (List<String>) cache.get("A").getValue(); 
     retrievedList1.add("From Thread 1"); 

    /* Meanwhile, someone puts something in the cache: */ 
     cache.put(new Element("C", new ArrayList<String>())); 

    Thread.sleep(2000); // Once again, we wait a bit, until "A" is evicted. 

    /* Now the following happens in Thread 2: */ 
     final List<String> retrievedList2 = 
        (List<String>) cache.get("A").getValue(); 
     retrievedList2.add("From Thread 2"); 
     cache.put(new Element("A", retrievedList2)); 

    /* Meanwhile in Thread 1: */  
     cache.put(new Element("A", retrievedList1)); 

    /* Now let's see the result: */ 
    final List<String> resultingList = 
         (List<String>) cache.get("A").getValue(); 
    for (final String string : resultingList) { 
     System.out.println(string); 
    } /* Prints only "From Thread 1". "From Thread 2" is lost. 
       But try it with maxElementsInMemory="3", too!! */ 

    CacheManager.getInstance().shutdown(); 
} 

que utilizaron los siguientes en ehcache.xml:

<cache name="smallCache" 
     maxElementsInMemory="1" 
     eternal="true" 
     overflowToDisk="true" 
     diskPersistent="true" 
     maxElementsOnDisk="200" 
     memoryStoreEvictionPolicy="LRU" 
     transactionalMode="off" 
     > 
</cache> 

Una solución puede ser el uso de Explicit Locking, que parece estar disponible para el autónomo (no-terracota) cachés, también (desde ehcache 2.1).

Otra solución sería tener solo un hilo que pueda modificar la Lista. Si tiene varios hilos que pueden modificarlo, y no usa el bloqueo en el caché, ¡puede obtener exactamente los resultados indeterminados que describió!

+0

Eso no muestra que la memoria caché devuelve dos copias diferentes de la lista, que muestra que tiene una y luego lee una desde el disco. – Gandalf

+0

@Gandalf: haz lo mismo dos veces: realiza otro enjuague() + Subproceso.sueño() y luego recupéralo nuevamente. Obtendrás dos copias diferentes. –

+0

@Gandalf: He editado el ejemplo un poco más (simulando múltiples hilos) para dejar absolutamente claro cómo puede ocurrir el problema. Ahora es un poco más largo, pero muestra exactamente a qué le temías en tu pregunta. Por favor, inténtelo con maxElementsInMemory = "3" también, ¡y vea la diferencia! –

2

Tengo un enfoque diferente para usted, que acabo de leer en un artículo sobre búsquedas de rango geográfico.

Ponga dos pares de valores-clave en la caché: uno con solo x como la clave y otro con xey como la clave. Cuando mira en el caché, primero busque la clave x-y-y. Si está allí, encontraste una pareja perfecta. Si no está allí, busque la tecla xy posiblemente encuentre una coincidencia con un valor de y diferente.

+0

+1 para el pensamiento creativo - como el enfoque –

+1

La memoria caché es de aproximadamente 15 millones de objetos, por lo que preferiría no crecer a 30 millones a menos que no haya otra forma. Sin embargo, lo tendré en cuenta. – Gandalf

+0

Además, ¿cómo funciona el caché con solo 'x' como la clave, ya que puedo tener varios objetos con el mismo valor 'x' (pero diferente 'y'). – Gandalf

1

me gustaría crear un método para obtener el valor de su objeto. Use un semáforo para restringir el acceso al método (o use sincronizado).

En su método, pruebe las coincidencias solo X, y si eso arroja resultados múltiples, envíe un texto para coincidencias XY.

Una vez que el objeto está fuera de la memoria caché, cualquier modificación en el objeto también modificará el objeto dentro de la memoria caché (ya que apuntan a la misma instancia).

Si quiere tener mucho cuidado, use métodos sincronizados para obtener/establecer las variables miembro dentro de MyObject, e incluya un bloqueo que sea la instancia de MyObject.

public void setX(int x) { 
    synchronized(this) { 
     this.x = x; 
    } 
} 
0
  • hacer una clase clave de x y y, es decir.class Key { int x,y }
  • implementa una operación de comparación separada para usted "orden léxico" en x y y,
  • ponerlo en un Map<Key,Value>
Cuestiones relacionadas