1. مقدمة
في هذه المقالة ، دعونا نلقي نظرة على الكافيين - مكتبة Java Cache عالية الأداء.
أحد الاختلافات الأساسية بين ذاكرة التخزين المؤقت والخريطة هو أن ذاكرة التخزين المؤقت يمكنها إعادة تدوير العناصر المخزنة.
تتمثل سياسة إعادة التدوير في حذف الكائنات في وقت محدد. تؤثر هذه الاستراتيجية بشكل مباشر على معدل ضرب ذاكرة التخزين المؤقت - وهي ميزة مهمة في مكتبة ذاكرة التخزين المؤقت.
يوفر الكافيين معدل ضرب شبه الأمثل بسبب استخدامه لاستراتيجية إعادة تدوير Tinylfu النافذة.
2. الاعتماد
نحتاج إلى إضافة تبعية الكافيين في pom.xml:
<Rependency> <roupeD> com.github.ben-manes.caffeine </rougiD> <StifactId> caffeine </shintifactid> <الإصدار> 2.5.5 </version> </rependency>
يمكنك العثور على أحدث إصدار من الكافيين على Maven Central.
3. املأ ذاكرة التخزين المؤقت
دعنا نلقي نظرة على استراتيجيات ملء ذاكرة التخزين المؤقت الثلاثة في الكافيين: التحميل اليدوي ، التحميل المتزامن ، والتحميل غير المتزامن.
أولاً ، نكتب فئة لتخزين نوع القيمة في ذاكرة التخزين المؤقت:
فئة DataObject {Private Final String Data ؛ int static int static private = 0 ؛ // Constructors/Getters public static dataObject get (string data) {ObjectCounter ++ ؛ إرجاع DataObject (البيانات) ؛ }}3.1. ملء يدوي
في هذه الاستراتيجية ، نضع القيمة يدويًا في ذاكرة التخزين المؤقت قبل استردادها.
دعونا نهيئة ذاكرة التخزين المؤقت:
ذاكرة التخزين المؤقت <string ، dataObject> cache = caffeine.newbuilder () .expirafterwrite (1 ، timeUnit.minutes) .maximumsize (100) .build () ؛
الآن يمكننا استخدام طريقة getIfpresent للحصول على بعض القيم من ذاكرة التخزين المؤقت. إذا كانت هذه القيمة غير موجودة في ذاكرة التخزين المؤقت ، فإن هذه الطريقة تُرجع NULL:
string key = "a" ؛ dataObject dataObject = cache.getIfpresent (مفتاح) ؛ assertnull (dataObject) ؛
يمكننا استخدام طريقة PUT لملء ذاكرة التخزين المؤقت يدويًا:
cache.put (مفتاح ، dataObject) ؛ dataObject = cache.getIfpresent (مفتاح) ؛ assertnotnull (dataObject) ؛
يمكننا أيضًا استخدام طريقة GET للحصول على القيمة ، التي تمرر وظيفة باستخدام مفتاح المعلمة كمعلمة. إذا لم يكن المفتاح موجودًا في ذاكرة التخزين المؤقت ، فسيتم استخدام الوظيفة لتوفير قيمة احتياطية ، يتم إدراجها في ذاكرة التخزين المؤقت بعد الحساب:
DataObject = cache .get (المفتاح ، k -> dataObject.get ("بيانات لـ A")) ؛ assertnotnull (dataObject) ؛ assertequals ("بيانات لـ" ، dataObject.getData ()) ؛يمكن أن تنفذ طريقة الحصول على الحسابات ذريًا. هذا يعني أنك تقوم فقط بالحساب مرة واحدة - حتى إذا طلبت مؤشرات ترابط متعددة القيمة في نفس الوقت. هذا هو السبب في أن استخدام GET أفضل من GetIfpresent.
في بعض الأحيان نحتاج إلى إبطال بعض القيم المخبأة يدويًا:
cache.invalidate (key) ؛ dataObject = cache.getifpresent (مفتاح) ؛ assertnull (dataObject) ؛
3.2. تحميل متزامن
تستخدم طريقة تحميل ذاكرة التخزين المؤقت هذه طريقة GET مع استراتيجية يدوية مماثلة للوظيفة المستخدمة لتهيئة القيم. دعونا نرى كيفية استخدامه.
أولاً ، نحتاج إلى تهيئة ذاكرة التخزين المؤقت:
loadingCache <string ، dataObject> cache = caffeine.newbuilder () .maximumsize (100) .expirafterwrite (1 ، timeUnit.minutes) .build (k -> dataObject.get ("بيانات لـ" + k)) ؛الآن يمكننا استخدام طريقة GET لاسترداد القيمة:
DataObject dataObject = cache.get (مفتاح) ؛ assertnotnull (dataObject) ؛ assertequals ("بيانات لـ" + مفتاح ، dataObject.getData ()) ؛يمكننا أيضًا استخدام طريقة getall للحصول على مجموعة من القيم:
MAP <string ، dataObject> dataObjectMap = cache.getall (arrays.aslist ("a" ، "b" ، "c")) ؛ assertequals (3 ، dataObjectMap.size ()) ؛استرداد القيم من وظيفة تهيئة الواجهة الخلفية الأساسية التي تم تمريرها إلى طريقة الإنشاء. هذا يسمح باستخدام ذاكرة التخزين المؤقت كواجهة رئيسية للوصول إلى القيم.
3.3. تحميل غير متزامن
هذه السياسة تفعل نفس الشيء كما كان من قبل ، ولكنها تنفذ العملية بشكل غير متزامن وتُرجع إلى قابلية للإكمال التي تحتوي على القيمة:
asyncloadingcache <string ، dataObject> cache = caffeine.newbuilder () .MaximumSize (100) .expirafterwrite (1 ، timeUnit.minutes) .buildasync (k -> dataObject.get ("data for" + k)) ؛يمكننا استخدام أساليب Get و Getall بنفس الطريقة ، مع الأخذ في الاعتبار أنهم يعيدون إكمالًا قابلاً للإكمال:
مفتاح السلسلة = "A" ؛ cache.get (مفتاح) .ThenAccept (dataObject -> {AssertNotnull (dataObject) ؛ assertequals ("Data for" + key ، dataObject.getData ()) ؛}) ؛ cache.getall (arrays.aslist ("a" ، "b" ، "c"))).يحتوي CompleteFuture على العديد من واجهات برمجة التطبيقات المفيدة ، ويمكنك الحصول على المزيد في هذه المقالة.
4. استرداد القيمة
لدى الكافيين ثلاث استراتيجيات لاسترداد القيمة: القائمة على الحجم ، القائمة على الوقت ، والمرجعية.
4.1. إعادة التدوير على أساس الحجم
تفترض طريقة إعادة التدوير هذه أن إعادة التدوير تحدث عند تجاوز حد حجم ذاكرة التخزين المؤقت المكونة. هناك طريقتان للحصول على الحجم: عد الكائن في ذاكرة التخزين المؤقت ، أو الحصول على الوزن.
دعونا نرى كيفية حساب الكائنات في ذاكرة التخزين المؤقت. عند تهيئة ذاكرة التخزين المؤقت ، يكون حجمه يساوي الصفر:
loadingCache <string ، dataObject> cache = caffeine.newbuilder () .maximumsize (1) .build (k -> dataObject.get ("data for" + k)) ؛ assertequals (0 ، cache.ExtimatedSize ()) ؛عندما نضيف قيمة ، يزيد الحجم بشكل كبير:
cache.get ("a") ؛ assertequals (1 ، cache.ExtimatedSize ()) ؛يمكننا إضافة القيمة الثانية إلى ذاكرة التخزين المؤقت ، والتي تتسبب في حذف القيمة الأولى:
cache.get ("b") ؛ cache.cleanup () ؛ assertequals (1 ، cache.ExtimatedSize ()) ؛تجدر الإشارة إلى أنه قبل الحصول على حجم ذاكرة التخزين المؤقت ، نسمي طريقة التنظيف. وذلك لأن إعادة تدوير ذاكرة التخزين المؤقت يتم تنفيذها بشكل غير متزامن ، وهذا النهج يساعد في انتظار إكمال إعادة التدوير.
يمكننا أيضًا تمرير وظيفة وزن للحصول على حجم ذاكرة التخزين المؤقت:
loadingCache <string ، dataObject> cache = caffeine.newbuilder () .maximumweight (10). Weighter ((k ، v) -> 5) .build (k -> dataObject.get ("data for" + k)) ؛ assertequals (0 ، cache.ExtimatedSize ()) ؛ cache.get ("a") ؛ assertequals (1 ، cache.ExtimatedSize ()) ؛ cache.get ("b") ؛ assertequals (2 ، cache.ExtimatedSize ()) ؛عندما يتجاوز الوزن 10 ، يتم حذف القيمة من ذاكرة التخزين المؤقت:
cache.get ("C") ؛ cache.cleanup () ؛ assertequals (2 ، cache.ExtimatedSize ()) ؛4.2. بناء على استرداد الوقت
تعتمد استراتيجية إعادة التدوير هذه على وقت انتهاء الصلاحية ، وهناك ثلاثة أنواع:
دعنا نؤكد سياسة انتهاء صلاحية ما بعد الوصول باستخدام طريقة expireeavterAccess:
loadingCache <string ، dataObject> cache = caffeine.newbuilder () .expirafterAccess (5 ، timeUnit.minutes) .build (k -> dataObject.get ("بيانات لـ" + k)) ؛لتكوين سياسة انتهاء صلاحية ما بعد الواجهة ، نستخدم طريقة expireexterwrite:
cache = caffeine.newbuilder () .expirafterwrite (10 ، timeUnit.Seconds) .weakkeys () .weakvalues () .build (k -> dataObject.get ("Data for" + k)) ؛لتهيئة سياسة مخصصة ، نحتاج إلى تنفيذ واجهة انتهاء الصلاحية:
cache = caffeine.newbuilder (). Exprieafter (انتهاء صلاحية جديد <string ، dataObject> () {Override public public expireaftercreate (مفتاح السلسلة ، قيمة dataObject ، الوقت الطويل الوقت) CurrentDuration ؛4.3. إعادة التدوير على أساس المرجع
يمكننا تكوين ذاكرة التخزين المؤقت لتمكين جمع القمامة لقيم المفاتيح المخزنة مؤقتًا. للقيام بذلك ، نقوم بتكوين المفتاح والقيمة كمراجع ضعيفة ، ويمكننا تكوين المراجع اللينة فقط لجمع القمامة.
عندما لا تكون هناك إشارات قوية إلى الكائن ، يمكن أن يؤدي استخدام الضعف إلى تمكين جمع القمامة من الكائنات. يتيح Softreference الكائنات تجميع القمامة استنادًا إلى سياسة JVM العالمية الأقل استخدامًا. لمزيد من التفاصيل حول استشهادات Java ، انظر هنا.
يجب أن نمكّن كل خيار باستخدام الكافيين.
loadingCache <string ، dataObject> cache = caffeine.newbuilder () .expirafterWrite (10 ، timeUnit.Seconds) .weakkeys () .weakvalues () .build (k -> dataObject.get ("data for" + k)) ؛ cache = caffeine.newbuilder () .expirafterwrite (10 ، timeUnit.Seconds) .SoftValues () .build (k -> dataObject.get ("بيانات لـ" + k)) ؛5. تحديث
يمكن تكوين ذاكرة التخزين المؤقت لتحديث الإدخال تلقائيًا بعد فترة زمنية محددة. دعونا نرى كيفية استخدام طريقة RefreshAfterWrite:
caffeine.newbuilder () .refreshafterWrite (1 ، timeUnit.minutes) .Build (k -> dataObject.get ("data for" + k)) ؛هنا يجب أن نفهم الفرق بين Expireafter و RefreshAfter. عند طلب إدخال منتهية الصلاحية ، سيتم حظر التنفيذ حتى تحسب وظيفة الإنشاء القيمة الجديدة.
ومع ذلك ، إذا كان يمكن تحديث الإدخال ، فإن ذاكرة التخزين المؤقت تقوم بإرجاع القيمة القديمة وإعادة تحميل القيمة بشكل غير متزامن.
6. الإحصاءات
الكافيين لديه وسيلة لتسجيل استخدام ذاكرة التخزين المؤقت:
loadingCache <string ، dataObject> cache = caffeine.newbuilder () .maximumsize (100) .RecordStats () .build (k -> dataObject.get ("بيانات لـ" + k)) ؛ cache.get ("a") ؛ cache.get ("a") ؛ assertequals (1 ، cache.stats (). hitCount ()) ؛ assertequals (1 ، cache.stats (). misscount ()) ؛قد نقوم أيضًا بتمرير مورد RecordStats لإنشاء تنفيذ STATSCONTER. يتم دفع هذا الكائن كل تغيير مرتبط بالإحصاء.
7. الخلاصة
في هذه المقالة ، نحن على دراية بمكتبة ذاكرة التخزين المؤقت للكافيين في Java. لقد رأينا كيفية تكوين ذاكرة التخزين المؤقت وملءها ، وكيفية اختيار سياسة انتهاء الصلاحية أو التحديث المناسبة بناءً على احتياجاتنا.
يمكن العثور على رمز المصدر للأمثلة في هذه المقالة على Github.
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.