1. Introducción
En este artículo, echemos un vistazo a la cafeína: una biblioteca Java Cache de alto rendimiento.
Una diferencia fundamental entre caché y mapa es que los cachés pueden reciclar artículos almacenados.
La política de reciclaje es eliminar objetos en un tiempo especificado. Esta estrategia afecta directamente la tasa de accesorios de caché, una característica importante de la biblioteca de caché.
La cafeína proporciona una tasa de golpes casi óptima debido al uso de la estrategia de reciclaje de tinylfu de ventana.
2. Dependencia
Necesitamos agregar la dependencia de la cafeína en pom.xml:
<Spendency> <MoupRid> com.github.ben-menes.caffeine </proupid> <artifactid> cafeine </arfactid> <versión> 2.5.5 </versión> </dependencia>
Puede encontrar la última versión de Caffeine en Maven Central.
3. Complete el caché
Echemos un vistazo a las tres estrategias de llenado de caché de la cafeína: carga manual, carga sincrónica y carga asincrónica.
Primero, escribimos una clase para el tipo de valor que se almacenará en el caché:
clase DataObject {datos de cadena final privada; Private static int ObjectCounter = 0; // Constructores estándar/getters public static dataObject get (string data) {ObjectCounter ++; devolver nuevo dataObject (datos); }}3.1. Relleno manual
En esta estrategia, colocamos manualmente el valor en el caché antes de recuperarlo.
Inicialicemos el caché:
Cache <String, dataObject> cache = caffeine.newBuilder () .ExpeAfTterSwrite (1, TimeUnit.Minutes) .maximumSize (100) .Build ();
Ahora podemos usar el método getififpresente para obtener algunos valores de la memoria caché. Si este valor no existe en el caché, este método devuelve nulo:
String key = "a"; dataObject dataObject = cache.getifPresent (clave); ASDERTNULL (dataObject);
Podemos usar el método Put para llenar manualmente el caché:
cache.put (clave, dataObject); dataObject = cache.getifpresent (clave); afirmarnotnull (dataObject);
También podemos usar el método GET para obtener el valor, que pasa una función con la tecla de parámetro como parámetro. Si la clave no existe en el caché, la función se utilizará para proporcionar un valor respaldo, que se inserta en el caché después del cálculo:
dataObject = cache .get (key, k -> dataObject.get ("data for a")); afirmarnotnull (dataObject); afirmarequals ("Data for a", dataObject.getData ());El método GET puede realizar cálculos atómicamente. Esto significa que solo hace el cálculo una vez, incluso si varios subprocesos solicitan el valor al mismo tiempo. Es por eso que usar Get es mejor que getifPresent.
A veces necesitamos invalidar manualmente algunos valores en caché:
cache.Invalidate (clave); dataObject = cache.getifpresent (clave); ASDERTNULL (dataObject);
3.2. Carga sincrónica
Este método de carga caché utiliza un método GET con una estrategia manual similar a la función utilizada para inicializar los valores. Veamos cómo usarlo.
Primero, necesitamos inicializar el caché:
LoadingCache <String, dataObject> cache = caffeine.newbuilder () .maximumSize (100) .expirafterwrite (1, timeUnit.minutes) .Build (k -> dataObject.get ("datos para" + k));Ahora podemos usar el método GET para recuperar el valor:
DataObject dataObject = cache.get (clave); afirmarnotnull (dataObject); afirmarequals ("datos para" + clave, dataObject.getData ());También podemos usar el método Getall para obtener un conjunto de valores:
Map <string, dataObject> dataObjectMap = cache.getall (arrays.aslist ("a", "b", "c")); afirmarequals (3, dataObjectMap.size ());Recupere los valores de la función de inicialización de backend subyacente pasada al método de compilación. Esto permite el uso de caché como la fachada principal de los valores de acceso.
3.3. Carga asíncrona
Esta política hace lo mismo que antes, pero realiza la operación de forma asincrónica y devuelve una Future Complete que contiene el valor:
Asyncloadingcache <string, dataObject> cache = caffeine.newBuilder () .maximumSize (100) .ExpireAftterwrite (1, TimeUnit.Minutes) .BuilderSync (k -> dataObject.get ("Datos para" + k));Podemos usar los métodos Get y Getall de la misma manera, teniendo en cuenta que están devolviendo una Future Complete:
Clave de cadena = "A"; cache.get (clave) .ThenAccept (dataObject -> {afirmoNotNull (dataObject); afirmarequals ("data para" + key, dataObject.getData ());}); cache.getall (arrays.aslist ("a", "b", "c"))) .ThenAcept (dataObjectMap -> afirmarequals (3, dataObjectMap.size ()));CompleteFuture tiene muchas API útiles, y puede obtener más en este artículo.
4. Recuperación de valor
La cafeína tiene tres estrategias de recuperación de valor: basadas en el tamaño, basadas en el tiempo y basadas en referencia.
4.1. Reciclaje basado en el tamaño
Este método de reciclaje supone que el reciclaje ocurre cuando se excede el límite de tamaño de caché configurado. Hay dos formas de obtener el tamaño: cuente el objeto en el caché o obtenga el peso.
Veamos cómo calcular objetos en el caché. Cuando se inicializa el caché, su tamaño es igual a cero:
LoadingCache <String, dataObject> cache = caffeine.newBuilder () .maximumSize (1) .Build (k -> dataObject.get ("datos para" + k)); afirmarequals (0, cache.estimatedSize ());Cuando agregamos un valor, el tamaño aumenta significativamente:
cache.get ("a"); afirmarequals (1, cache.estimatedSize ());Podemos agregar el segundo valor al caché, lo que hace que se elimine el primer valor:
cache.get ("b"); Cache.CleanUp (); afirmarequals (1, cache.estimatedSize ());Vale la pena mencionar que antes de obtener el tamaño del caché, llamamos al método de limpieza. Esto se debe a que el reciclaje de caché se ejecuta asincrónicamente, y este enfoque ayuda a esperar a que el reciclaje se complete.
También podemos pasar una función de pescador para obtener el tamaño de la memoria caché:
LoadingCache <String, dataObject> cache = caffeine.newBuilder () .maximumweight (10) .weighter ((k, v) -> 5) .build (k -> dataObject.get ("data para" + k)); afirmarequals (0, cache.estimatedSize ()); cache.get ("a"); afirmarequals (1, cache.estimatedSize ()); cache.get ("b"); afirmarequals (2, cache.estimatedsize ());Cuando el peso excede los 10, el valor se elimina del caché:
cache.get ("c"); Cache.CleanUp (); afirmarequals (2, cache.estimatedsize ());4.2. Basado en la recuperación del tiempo
Esta estrategia de reciclaje se basa en el tiempo de vencimiento de la entrada, y hay tres tipos:
Configuremos la política de expiración posterior al acceso utilizando el método expirante de los procesos:
LoadingCache <String, dataObject> cache = caffeine.newbuilder () .expirafterAccess (5, timeUnit.minutes) .Build (k -> dataObject.get ("data para" + k));Para configurar la política de vencimiento posterior a la escritura, utilizamos el método expirante de Write:
Cache = Caffeine.newBuilder () .ExpeAftterWrite (10, TimeUnit.Seconds) .weakkeys () .weakValues () .Build (k -> dataObject.get ("datos para" + k));Para inicializar una política personalizada, necesitamos implementar la interfaz de vencimiento:
cache = caffeine.newBuilder (). expirante (new Expiry <String, dataObject> () {@Override public Long ExpirAfTterCreate (clave de cadena, valor de dataObject, Long CurrentTime) {return value.getData (). longitud () * 1000;} @Override public Long Long expirupdate (clave de cadena, value de datos, value de larga CurrentDuration;4.3. Reciclaje basado en referencia
Podemos configurar el caché para habilitar la recolección de basura de los valores de claves en caché. Para hacer esto, configuramos la clave y el valor como referencias débiles, y podemos configurar solo referencias suaves para la recolección de basura.
Cuando no hay referencias fuertes al objeto, el uso de la Lefrefencia Weakrefence puede habilitar la recolección de basura de objetos. Softreference permite que los objetos se recolecten basura en función de la política global de usadas de menos recursos de JVM. Para obtener más detalles sobre las citas de Java, vea aquí.
Debemos habilitar cada opción usando Caffeine.WeakKeys (), Caffeine.WeakValues () y Caffeine.SoftValues ()::
LoadingCache <String, dataObject> cache = caffeine.newbuilder () .expirantefterwrite (10, timeUnit.seconds) .weakkeys () .weakvalues () .build (k -> dataObject.get ("datos para" + k)); Cache = Caffeine.newBuilder () .ExpeAftterWrite (10, TimeUnit.Seconds) .SoftValues () .Build (k -> dataObject.get ("datos para" + k));5. Actualizar
El caché se puede configurar para actualizar automáticamente la entrada después de un período de tiempo definido. Veamos cómo usar el método RefreshAfterWrite:
Caffeine.newBuilder () .RefreshAfterWrite (1, TimeUnit.Minutes) .Build (k -> dataObject.get ("datos para" + k));Aquí debemos entender la diferencia entre expiración y refrescante. Cuando se solicita una entrada vencida, la ejecución se bloqueará hasta que la función de compilación calcule el nuevo valor.
Sin embargo, si la entrada se puede actualizar, el caché devuelve un valor antiguo y vuelve a cargar el valor de forma asincrónica.
6. Estadísticas
La cafeína tiene una forma de registrar el uso de caché:
LoadingCache <String, dataObject> cache = caffeine.newBuilder () .maximumSize (100) .RecordStats () .Build (k -> dataObject.get ("data para" + k)); cache.get ("a"); cache.get ("a"); afirmarequals (1, cache.stats (). hitcount ()); afirmarequals (1, cache.stats (). MissCount ());También podemos pasar al proveedor de registros de registros para crear una implementación de STATSCounter. Este objeto se presiona cada cambio relacionado con estadística.
7. Conclusión
En este artículo, estamos familiarizados con la biblioteca de caché de cafeína de Java. Vimos cómo configurar y llenar el caché, y cómo elegir la cadena de vencimiento o actualizar la política adecuada en función de nuestras necesidades.
El código fuente para los ejemplos en este artículo se puede encontrar en GitHub.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.