1. Introdução
Neste artigo, vamos dar uma olhada na cafeína - uma biblioteca de cache Java de alto desempenho.
Uma diferença fundamental entre cache e mapa é que os caches podem reciclar itens armazenados.
A política de reciclagem é excluir objetos em um horário especificado. Essa estratégia afeta diretamente a taxa de acerto do cache - um recurso importante da biblioteca de cache.
A cafeína fornece uma taxa de acerto quase ideal devido ao uso da estratégia de reciclagem de tinilfu de janela.
2. Dependência
Precisamos adicionar a dependência da cafeína no pom.xml:
<Ependency> <PuerpId> com.github.ben-manes.caffeine </foupid> <stifactId> cafeína </sutifactId> <versão> 2.5.5 </versão> </dependency>
Você pode encontrar a versão mais recente da cafeína no Maven Central.
3. Preencha o cache
Vamos dar uma olhada nas três estratégias de preenchimento de cache da cafeína: manual, carregamento síncrono e carga assíncrona.
Primeiro, escrevemos uma classe para que o tipo de valor seja armazenado no cache:
classe DataObject {private final String Data; private static int objectCounter = 0; // Construtores/getters padrão public estático dataObject get (String Data) {objectCounter ++; retornar novo DataObject (dados); }}3.1. Recheio manual
Nesta estratégia, colocamos manualmente o valor no cache antes de recuperá -lo.
Vamos inicializar o cache:
Cache <string, dataObject> cache = cafeine.newbuilder () .expireAthterWrite (1, timeUnit.minutes) .Maximumsize (100) .build ();
Agora podemos usar o método getifpresent para obter alguns valores do cache. Se esse valor não existir no cache, esse método retornará nulo:
String key = "A"; DataObject DataObject = cache.getifpresent (key); assertNull (DataObject);
Podemos usar o método put para preencher manualmente o cache:
cache.put (key, DataObject); DataObject = cache.getifpresent (key); assertnotNull (DataObject);
Também podemos usar o método get para obter o valor, que passa uma função com a chave do parâmetro como um parâmetro. Se a chave não existir no cache, a função será usada para fornecer um valor de fallback, que é inserido no cache após o cálculo:
DataObject = cache .get (key, k -> DataObject.get ("Dados para A")); assertnotNull (DataObject); asserTequals ("dados para um", DataObject.getData ());O método GET pode executar cálculos atomicamente. Isso significa que você faz apenas o cálculo uma vez - mesmo que vários threads solicitem o valor ao mesmo tempo. É por isso que o uso do get é melhor do que o Getifpresent.
Às vezes, precisamos invalidar manualmente alguns valores em cache:
cache.invalidate (key); dataObject = cache.getifpresent (key); assertNull (DataObject);
3.2. Carga síncrona
Esse método de carregamento de cache usa um método GET com uma estratégia manual semelhante à função usada para inicializar valores. Vamos ver como usá -lo.
Primeiro, precisamos inicializar o cache:
Carregingcache <string, dataObject> cache = cafeine.newbuilder () .maximumsize (100) .expireAthterWrite (1, timeUnit.Minutes) .build (k -> DataObject.get ("dados para" + k));Agora podemos usar o método get para recuperar o valor:
DataObject DataObject = cache.get (key); assertnotNull (DataObject); assertequals ("dados para" + key, DataObject.getData ());Também podemos usar o método getall para obter um conjunto de valores:
Mapa <string, DataObject> DataObjectMap = cache.getall (Arrays.asList ("A", "B", "C")); assertequals (3, DataObjectMap.size ());Recuperar valores da função de inicialização do back -end subjacente passada para o método de construção. Isso permite o uso do cache como a fachada principal do acesso aos valores.
3.3. Carregamento assíncrono
Esta política faz a mesma coisa de antes, mas executa a operação de forma assíncrona e retorna uma completa completa contendo o valor:
AsyncloadingCache <String, DataObject> cache = Caffeine.newbuilder () .Maximumsize (100) .ExpireAthterWrite (1, timeUnit.Minutes) .buildAsync (k -> dataObject.get ("Dados para" + k));Podemos usar os métodos GET e GETALL da mesma maneira, levando em consideração que eles estão retornando um FULTE FULLABLE:
String key = "A"; cache.get (key) .ThenAccept (DataObject -> {AssertNotNull (DataObject); Assertequals ("Dados para" + key, DataObject.getData ());}); Cache.getall (Arrays.asList ("A", "B", "C"))) .ThenCept (DataObjectMap -> Assertequals (3, DataObjectMap.size ()));O CompletFuture possui muitas APIs úteis e você pode obter mais neste artigo.
4. Recuperação de valor
A cafeína possui três estratégias de recuperação de valor: baseada em tamanho, baseada em tempo e baseada em referência.
4.1. Reciclagem com base no tamanho
Esse método de reciclagem assume que a reciclagem ocorre quando o limite de tamanho do cache configurado é excedido. Existem duas maneiras de obter o tamanho: conte o objeto no cache ou obter o peso.
Vamos ver como calcular objetos no cache. Quando o cache é inicializado, seu tamanho é igual a zero:
Carregingcache <string, dataObject> cache = Caffeine.newbuilder () .Maximumsize (1) .build (k -> dataObject.get ("Data para" + k)); assertequals (0, cache.estimatedSize ());Quando agregamos um valor, o tamanho aumenta significativamente:
cache.get ("A"); assertequals (1, cache.estimatedSize ());Podemos adicionar o segundo valor ao cache, o que faz com que o primeiro valor seja excluído:
cache.get ("b"); cache.cleanup (); assertequals (1, cache.estimatedSize ());Vale ressaltar que, antes de obter o tamanho do cache, chamamos o método de limpeza. Isso ocorre porque a reciclagem de cache é executada de forma assíncrona, e essa abordagem ajuda a aguardar a conclusão da reciclagem.
Também podemos passar por uma função de peso para obter o tamanho do cache:
Carregingcache <string, dataObject> cache = cafeine.newbuilder () .maximumweight (10) .weighight ((k, v) -> 5) .build (k -> dataObject.get ("dados para" + k)); assertequals (0, cache.estimatedSize ()); cache.get ("A"); assertequals (1, cache.estimatedSize ()); cache.get ("b"); assertequals (2, cache.estimatedSize ());Quando o peso excede 10, o valor é excluído do cache:
cache.get ("c"); cache.cleanup (); assertequals (2, cache.estimatedSize ());4.2. Com base na recuperação de tempo
Essa estratégia de reciclagem é baseada no tempo de expiração da entrada e existem três tipos:
Vamos configurar a política de expiração pós-acesso usando o método expireafterAccess:
CarregingCache <String, DataObject> cache = Caffeine.newbuilder () .expireabterAccess (5, timeUnit.Minutes) .build (k -> DataObject.get ("Dados para" + k));Para configurar a política de expiração pós-gravação, usamos o método ExpireafterWrite:
cache = cafeína.newbuilder () .expireAthterWrite (10, timeUnit.Seconds) .WeakKeys () .WeakValues () .build (k -> dataObject.get ("dados para" + k));Para inicializar uma política personalizada, precisamos implementar a interface de expiração:
cache = cafeine.newbuilder (). expireafter (novo expiry <string, dataObject> () {@Override public ExtirEafterCreate long long exTIREFRETE (chave de string, valor do nome de dados, longa dura e currentTime) {Return Value.getData (). CurrentDuration;4.3. Reciclagem baseada em referência
Podemos configurar o cache para ativar a coleta de lixo de valores de chave em cache. Para fazer isso, configuramos a chave e o valor como referências fracas e podemos configurar apenas referências suaves para a coleta de lixo.
Quando não há fortes referências ao objeto, o uso de fracos pode permitir a coleta de objetos de lixo. A Softreference permite que os objetos coletem lixo com base na política global de menos recentemente usada da JVM. Para mais detalhes sobre citações de java, veja aqui.
Devemos ativar cada opção usando cafeína.weakkeys (), cafeína.weakvalues () e cafeine.softValues ():
Carregingcache <string, DataObject> cache = cafeine.newbuilder () .expireAthterWrite (10, timeUnit.seconds) .weakkeys () .weakValues () .build (k -> dataObject.get ("dados para" + k)); cache = cafeína.newbuilder () .expireAthterWrite (10, timeUnit.Seconds) .SoftValues () .build (k -> dataObject.get ("dados para" + k));5. Atualizar
O cache pode ser configurado para atualizar automaticamente a entrada após um período de tempo definido. Vamos ver como usar o método refreshafterwrite:
Cafeine.newbuilder () .refreshafterwrite (1, timeUnit.Minutes) .build (k -> DataObject.get ("Dados para" + k));Aqui devemos entender a diferença entre o Expireafter e o RefreshAfter. Quando uma entrada expirada é solicitada, a execução será bloqueada até que a função de compilação calcule o novo valor.
No entanto, se a entrada puder ser atualizada, o cache retornará um valor antigo e recarregue o valor de forma assíncrona.
6. Estatísticas
A cafeína tem uma maneira de registrar o uso do cache:
Carregingcache <string, dataObject> cache = Caffeine.newbuilder () .maximumsize (100) .recordStats () .build (k -> dataObject.get ("dados para" + k)); cache.get ("a"); cache.get ("a"); assertequals (1, cache.stats (). HitCount ()); assertequals (1, cache.stats (). missCount ());Também podemos passar no RecordStats Fornecedor para criar uma implementação do StatScounter. Este objeto é empurrado todas as mudanças relacionadas à estatística.
7. Conclusão
Neste artigo, estamos familiarizados com a biblioteca de cache de cafeína da Java. Vimos como configurar e preencher o cache e como escolher a política de expiração ou atualização apropriada com base em nossas necessidades.
O código -fonte dos exemplos deste artigo pode ser encontrado no GitHub.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.