2010-11-09 15 views
6

Voici donc le problème que je suis en train de résoudre - j'ai un objet avec deux champs entiers que je veux mettre en cacheEhcache - à l'aide d'une liste <Integer> la valeur du cache

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

Maintenant, le champ x est ce que Je suis principalement sur - mais il peut y avoir des doublons, auquel cas je veux revenir sur le second champ (pour que this.x = that.x et this.y = that.y). y ne peut être que 25 valeurs distinctes. Maintenant, je sais que je pourrais juste combiner les deux comme une chaîne et l'utiliser comme clé de cache, mais alors je devrais essayer x+[25 possible values] pour déterminer si elle n'était pas dans le cache - ce qui rend le cache très cher. Je pensais essayer de stocker un List<Integer> comme la valeur de cache pour le champ x et puis si leur était plus d'un, parcourir la liste et rechercher une correspondance sur y. Maintenant, si j'utilise un ConcurrentList (ou un ensemble si je me soucie des doublons - ignorons cela pour le moment) plusieurs threads pourront-ils y ajouter et ensuite le remettre dans le cache sans conditions de course? Est-il possible qu'Ehcache renvoie deux objets List différents à deux threads, puis quand ils ajoutent leur nouvelle valeur à la liste et tentent de la remettre dans le cache, je pourrais obtenir des résultats indéterministes? Voyez-vous une meilleure façon de construire ce cache?

EDIT: J'apprécie les réponses ci-dessous, mais tout le monde semble manquer le point principal. Est-ce que ça va marcher? Est-ce qu'Ehcache peut renvoyer deux objets différents pour le même cacheKey (disons si l'objet était sur le disque pendant l'appel et qu'il est sérialisé deux fois, une fois pour chaque appel).

+0

Y a-t-il une raison pour laquelle vous ne pouvez pas utiliser un hachage comme clé? – Falmarri

+0

Comme je l'ai dit dans la question qui voudrait dire pour une valeur donnée de x, je devrais vérifier le cache peut-être 25 fois pour savoir que ce n'était pas dans le cache. Je veux faire correspondre sur x peu importe la valeur de y - mais s'il y a plusieurs x alors la meilleure valeur de y. – Gandalf

+0

Oui, il est possible d'avoir une valeur ajoutée deux fois en utilisant la méthode que vous avez mentionnée. Utilisez plutôt un ConcurrentMap. –

Répondre

4

Il est tout à fait possible que vous obtenez deux instances différentes de votre liste (ou de tout Serializable)! Essayez ceci:

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

J'ai utilisé ce qui suit dans ehcache.xml:

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

Une solution peut être d'utiliser Explicit Locking, qui semble être disponible pour les caches autonomes (non cuite), trop (depuis ehcache 2.1).

Une autre solution serait de n'avoir qu'un seul thread pouvant modifier la liste. Si vous avez plusieurs threads qui peuvent le modifier, et que vous n'utilisez pas le verrouillage sur le cache, alors vous pouvez obtenir exactement les résultats indéterministes que vous avez décrits!

+0

Cela n'affiche pas le cache retournant deux copies différentes de la liste, qui vous en montre une et ensuite en lit une à partir du disque. – Gandalf

+0

@Gandalf: Faites simplement la même chose deux fois: Faites un autre flush() + Thread.sleep(), puis récupérez-le à nouveau. Vous aurez deux copies différentes. –

+0

@Gandalf: J'ai modifié l'exemple un peu plus (simulant plusieurs threads) pour le rendre absolument clair, comment le problème peut se produire. C'est un peu plus long maintenant, mais cela montre exactement ce dont vous aviez peur dans votre question. S'il vous plaît essayez avec maxElementsInMemory = "3", aussi, et voyez la différence! –

2

J'ai une approche différente pour vous, que je viens de lire dans un article sur les recherches de zones géographiques.

Mettez deux paires de valeurs-clés dans le cache: une avec seulement x comme clé et une avec x et y comme clé. Lorsque vous regardez dans le cache, recherchez d'abord la clé x et y. Si c'est là, vous avez trouvé un match parfait. Si ce n'est pas le cas, recherchez la clé x et trouvez éventuellement une correspondance avec une valeur y différente.

+0

+ 1 pour la pensée créative - comme l'approche –

+1

Le cache est d'environ 15 millions d'objets - donc je préfère ne pas le faire passer à 30 millions à moins qu'il n'y ait pas d'autre moyen. Je garderai cela à l'esprit cependant. – Gandalf

+0

Aussi comment fonctionne le cache en utilisant simplement 'x' comme clé, puisque je peux avoir plusieurs objets avec la même valeur 'x' (mais différent 'y'). – Gandalf

1

Je voudrais créer une méthode pour obtenir la valeur de votre objet. Utilisez un sémaphore pour restreindre l'accès à la méthode (ou utilisez synchronisé).

Dans votre méthode, effectuez un test pour les correspondances X uniquement, et si cela renvoie plusieurs résultats, envoyez du texte pour les correspondances XY.

Une fois que l'objet est en dehors du cache, toute modification de l'objet modifie également l'objet dans le cache (puisqu'ils pointent vers la même instance).

Si vous voulez être très prudent, utilisez des méthodes synchronisées pour obtenir/définir les variables membres dans MyObject et inclure un verrou qui est l'instance MyObject.

public void setX(int x) { 
    synchronized(this) { 
     this.x = x; 
    } 
} 
0
  • Faire une classe clé de x et y, à savoir.class Key { int x,y }
  • mettre en œuvre une opération de comparaison séparée pour vous « commande lexical » sur x et y,
  • le mettre dans un Map<Key,Value>