Question

J'ai utilisé LinkedHashMap avec accessOrder true, tout en autorisant un maximum de 500 entrées à tout moment en tant que cache LRU pour les données. Mais, en raison de problèmes d’évolutivité, je souhaite passer à une solution de remplacement thread-safe. ConcurrentHashMap semble bien à cet égard, mais il ne possède pas les fonctionnalités de removeEldestEntry(Map.Entry e) et <=> trouvées dans <=>. Quelqu'un peut-il indiquer un lien ou m'aider à faciliter la mise en œuvre.

Était-ce utile?

La solution

J'ai récemment fait quelque chose de similaire avec ConcurrentHashMap<String,CacheEntry>, où CacheEntry enveloppe l'élément réel et ajoute des statistiques d'éviction de cache: délai d'expiration, délai d'insertion (pour l'éviction FIFO / LIFO), dernier temps utilisé (pour l'éviction LRU / MRU), nombre nombre de hits (pour une expulsion LFU / MFU), etc. L’expulsion réelle est synchronisée et crée un ArrayList<CacheEntry> et fait un Collections.sort () dessus en utilisant le comparateur approprié pour la stratégie d’expulsion. Comme cela coûte cher, chaque expulsion élimine les 5% inférieurs des CacheEntries. Je suis certain que le réglage des performances pourrait aider.

Dans votre cas, puisque vous utilisez la FIFO, vous pouvez conserver un élément distinct ConcurrentLinkedQueue . Lorsque vous ajoutez un objet à ConcurrentHashMap, effectuez un ConcurrentLinkedQueue.add () de cet objet. Lorsque vous souhaitez supprimer une entrée, faites un ConcurrentLinkedQueue.poll () pour supprimer l’objet le plus ancien, puis supprimez-le également de ConcurrentHashMap.

Mise à jour: d'autres possibilités dans ce domaine incluent les collections Java synchronisation. le wrapper et la version 1.6 de Java 1.6 ConcurrentSkipListMap .

Autres conseils

Avez-vous essayé d’utiliser l’une des nombreuses solutions de mise en cache, comme ehcache? Vous pouvez essayer d'utiliser LinkedHashMap avec un ReadWriteLock. Cela vous donnerait un accès en lecture simultané.

Cela peut sembler vieux maintenant, mais au moins juste pour mon propre suivi d'historique, je vais ajouter ma solution ici: j'ai combiné ConcurrentHashMap qui mappe K - & sous-classe de WeakReference, ConcurrentLinkedQueue, et une interface qui définit la désérialisation des objets de valeur en fonction de K pour exécuter correctement la mise en cache LRU. La file d'attente contient des références fortes et le CPG supprimera les valeurs de la mémoire si nécessaire. Le suivi de la taille de la file d'attente implique AtomicInteger, car vous ne pouvez pas vraiment inspecter la file d'attente pour déterminer quand expulser. Le cache gérera les expulsions / ajouts à la file d'attente, ainsi que la gestion des cartes. Si le GC a expulsé la valeur de la mémoire, la mise en œuvre de l'interface de désérialisation gérera la récupération de la valeur. J'ai également eu une autre implémentation qui impliquait de spooling sur disque / de relire ce qui était spoulé, mais c'était beaucoup plus lent que la solution que j'ai publiée ici, car je devais synchroniser spool / reading.

Vous avez mentionné vouloir résoudre les problèmes d'évolutivité avec un & "; thread-safe &"; alternative. " Sécurité du filetage " ici, cela signifie que la structure est tolérante à l’égard des tentatives d’accès simultané, en ce sens qu’elle ne subira pas de corruption par utilisation simultanée sans synchronisation externe. Cependant, une telle tolérance ne contribue pas nécessairement à améliorer & "L'évolutivité &"; Dans l’approche la plus simple - bien que généralement peu judicieuse -, vous essayez de synchroniser votre structure en interne tout en maintenant les opérations check-then-act non atomiques dangereuses.

Les caches LRU nécessitent au moins une certaine connaissance de la structure totale. Ils ont besoin de quelque chose comme un nombre de membres ou la taille des membres pour décider quand expulser, puis ils doivent pouvoir coordonner l'éviction avec des tentatives simultanées de lecture, d'ajout ou de suppression d'éléments. Essayer de réduire la synchronisation nécessaire pour un accès simultané au & Quot; main & Quot; La structure lutte contre votre mécanisme d’expulsion et force votre politique d’expulsion à être moins précise dans ses garanties.

La réponse actuellement acceptée mentionne & "lorsque vous souhaitez expulser une entrée &"; C'est là que réside le problème. Comment savez-vous quand vous voulez expulser une entrée? Quelles autres opérations devez-vous mettre en pause pour prendre cette décision?

Enveloppez la carte dans un Collections.synchronizedMap(). Si vous devez appeler des méthodes supplémentaires, synchronize sur la carte que vous avez renvoyée et appelez la méthode d'origine sur la carte d'origine ( voir les javadocs pour un exemple ). Il en va de même lorsque vous parcourez les clés, etc.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top