Pergunta

Eu tenho usado LinkedHashMap com accessOrder verdade juntamente com permitindo um máximo de 500 entradas a qualquer momento como o cache LRU de dados. Mas devido a problemas de escalabilidade que eu quero passar para alguma alternativa thread-safe. ConcurrentHashMap parece bom a este respeito, mas não tem as características de accessOrder e removeEldestEntry(Map.Entry e) encontrados em LinkedHashMap. Pode apontar alguém a algum link ou me ajudar a aliviar a implementação.

Foi útil?

Solução

Eu fiz algo semelhante recentemente com ConcurrentHashMap<String,CacheEntry>, onde CacheEntry envolve o item real e acrescenta estatísticas de despejo de cache: tempo de expiração, tempo de inserção (para LIFO despejo FIFO /), a última vez utilizada (por LRU / MRU despejo), o número de visitas (para despejo MFU LFU /), etc. o despejo real é sincronizado e cria uma ArrayList<CacheEntry> e faz um Collections.sort () nele usando o comparador apropriado para a estratégia de despejo. Uma vez que este é caro, cada despejo, em seguida, lops fora da parte inferior 5% dos CacheEntries. Tenho certeza de que o ajuste de desempenho ajudaria embora.

No seu caso, uma vez que você está fazendo FIFO, você poderia manter um separado ConcurrentLinkedQueue . Quando você adicionar um objeto para o ConcurrentHashMap, fazer um ConcurrentLinkedQueue.add () desse objeto. Quando você quer expulsar uma entrada, fazer um ConcurrentLinkedQueue.poll () para remover o objeto mais antigo, em seguida, removê-lo da ConcurrentHashMap também.

Update: Outras possibilidades nesta área incluem a Java Collections sincronização envoltório eo Java 1.6 ConcurrentSkipListMap .

Outras dicas

Você já tentou usar uma das muitas soluções de cache como ehcache? Você pode tentar usar LinkedHashMap com um ReadWriteLock. Isto lhe daria acesso simultâneo de leitura.

Isso pode parecer velho agora, mas pelo menos só para minha própria rastreamento história, eu estou indo para adicionar a minha solução aqui: eu combinei ConcurrentHashMap que mapeia K-> subclasse de WeakReference, ConcurrentLinkedQueue, e uma interface que define desserialização de os objetos de valor com base em K para executar LRU cache corretamente. A fila detém refs fortes, eo GC vai expulsar os valores da memória quando apropriado. Rastreando o tamanho da fila envolvidos AtomicInteger, como você não pode realmente inspecionar a fila para determinar quando despejar. O cache vai lidar com o despejo de / adicionar à fila, bem como a gestão mapa. Se o GC despejado o valor da memória, a implementação da interface desserialização irá lidar com recuperando o valor de volta. Eu também tive outra implementação que spool envolvido no disco / re-ler o que foi colocado em spool, mas isso foi muito mais lento do que a solução que eu postei aqui, como Ihad para sincronizar spool / leitura.

Você menciona querer resolver problemas de escalabilidade com uma alternativa "thread-safe". "Segmento de segurança" aqui significa que a estrutura é tolerante de tentativas de acesso simultâneo, na medida em que não sofrerão a corrupção pelo uso simultâneo sem sincronização externa. No entanto, tal tolerância não necessariamente ajudar a melhorar "escalabilidade". No mais simples - embora geralmente equivocada - abordagem, você vai tentar sincronizar sua estrutura interna e ainda deixar não atômica check-então-act operações inseguras

. caches

LRU exigem pelo menos alguma consciência da estrutura total. Eles precisam de algo como uma contagem dos membros ou o tamanho dos membros para decidir quando para expulsar, e então eles precisam ser capazes de coordenar o despejo com as tentativas simultâneas para ler, adicionar ou elementos remover. Tentando reduzir a sincronização necessária para o acesso simultâneo aos "principais" lutas estrutura contra o seu mecanismo de despejo, e as forças de sua política de remoção a ser menos precisos em suas garantias.

A resposta atualmente aceita menciona "quando você quer expulsar uma entrada". É aí que reside o busílis. Como você sabe quando você quer expulsar uma entrada? Que outras operações que você precisa para fazer uma pausa, a fim de tomar esta decisão?

Enrole o mapa em um Collections.synchronizedMap(). Se você precisa chamar métodos adicionais, em seguida, synchronize no mapa que você voltar de esta chamada, e chamar o método original no mapa original ( veja os javadocs para um exemplo ). O mesmo se aplica quando você iterar sobre as chaves, etc.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top