這篇文章將為大家詳細(xì)講解有關(guān)Java緩存使用如何實(shí)現(xiàn)的,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
網(wǎng)站設(shè)計(jì)制作過(guò)程拒絕使用模板建站;使用PHP+MYSQL原生開(kāi)發(fā)可交付網(wǎng)站源代碼;符合網(wǎng)站優(yōu)化排名的后臺(tái)管理系統(tǒng);成都網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)收費(fèi)合理;免費(fèi)進(jìn)行網(wǎng)站備案等企業(yè)網(wǎng)站建設(shè)一條龍服務(wù).我們是一家持續(xù)穩(wěn)定運(yùn)營(yíng)了十多年的成都創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司。
Java緩存主要有LRU和FIFO,LRU是Least Recently Used的縮寫,即最近最久未使用,F(xiàn)IFO就是先進(jìn)先出,下面就使用Java來(lái)實(shí)現(xiàn)這兩種緩存。
LRU
LRU緩存的思想
按照如上思想,可以使用LinkedHashMap來(lái)實(shí)現(xiàn)LRU緩存。
這是LinkedHashMap的一個(gè)構(gòu)造函數(shù),傳入的第三個(gè)參數(shù)accessOrder為true的時(shí)候,就按訪問(wèn)順序?qū)inkedHashMap排序,為false的時(shí)候就按插入順序,默認(rèn)是為false的。
當(dāng)把a(bǔ)ccessOrder設(shè)置為true后,就可以將最近訪問(wèn)的元素置于最前面,這樣就可以滿足上述的第二點(diǎn)。
/** * Constructs an empty LinkedHashMap instance with the * specified initial capacity, load factor and ordering mode. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @param accessOrder the ordering mode - true for * access-order, false for insertion-order * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
這是LinkedHashMap中另外一個(gè)方法,當(dāng)返回true的時(shí)候,就會(huì)remove其中最久的元素,可以通過(guò)重寫這個(gè)方法來(lái)控制緩存元素的刪除,當(dāng)緩存滿了后,就可以通過(guò)返回true刪除最久未被使用的元素,達(dá)到LRU的要求。這樣就可以滿足上述第三點(diǎn)要求。
protected boolean removeEldestEntry(Map.Entryeldest) { return false; }
由于LinkedHashMap是為自動(dòng)擴(kuò)容的,當(dāng)table數(shù)組中元素大于Capacity * loadFactor的時(shí)候,就會(huì)自動(dòng)進(jìn)行兩倍擴(kuò)容。但是為了使緩存大小固定,就需要在初始化的時(shí)候傳入容量大小和負(fù)載因子。
為了使得到達(dá)設(shè)置緩存大小不會(huì)進(jìn)行自動(dòng)擴(kuò)容,需要將初始化的大小進(jìn)行計(jì)算再傳入,可以將初始化大小設(shè)置為(緩存大小 / loadFactor) + 1,這樣就可以在元素?cái)?shù)目達(dá)到緩存大小時(shí),也不會(huì)進(jìn)行擴(kuò)容了。這樣就解決了上述第一點(diǎn)問(wèn)題。
通過(guò)上面分析,實(shí)現(xiàn)下面的代碼
import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class LRU1{ private final int MAX_CACHE_SIZE; private final float DEFAULT_LOAD_FACTORY = 0.75f; LinkedHashMap map; public LRU1(int cacheSize) { MAX_CACHE_SIZE = cacheSize; int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1; /* * 第三個(gè)參數(shù)設(shè)置為true,代表linkedlist按訪問(wèn)順序排序,可作為L(zhǎng)RU緩存 * 第三個(gè)參數(shù)設(shè)置為false,代表按插入順序排序,可作為FIFO緩存 */ map = new LinkedHashMap (capacity, DEFAULT_LOAD_FACTORY, true) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE; } }; } public synchronized void put(K key, V value) { map.put(key, value); } public synchronized V get(K key) { return map.get(key); } public synchronized void remove(K key) { map.remove(key); } public synchronized Set > getAll() { return map.entrySet(); } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); for (Map.Entry entry : map.entrySet()) { stringBuilder.append(String.format("%s: %s ", entry.getKey(), entry.getValue())); } return stringBuilder.toString(); } public static void main(String[] args) { LRU1 lru1 = new LRU1<>(5); lru1.put(1, 1); lru1.put(2, 2); lru1.put(3, 3); System.out.println(lru1); lru1.get(1); System.out.println(lru1); lru1.put(4, 4); lru1.put(5, 5); lru1.put(6, 6); System.out.println(lru1); } }
運(yùn)行結(jié)果:
從運(yùn)行結(jié)果中可以看出,實(shí)現(xiàn)了LRU緩存的思想。
接著使用HashMap和鏈表來(lái)實(shí)現(xiàn)LRU緩存。
主要的思想和上述基本一致,每次添加元素或者讀取元素就將元素放置在鏈表的頭,當(dāng)緩存滿了之后,就可以將尾結(jié)點(diǎn)元素刪除,這樣就實(shí)現(xiàn)了LRU緩存。
這種方法中是通過(guò)自己編寫代碼移動(dòng)結(jié)點(diǎn)和刪除結(jié)點(diǎn),為了防止緩存大小超過(guò)限制,每次進(jìn)行put的時(shí)候都會(huì)進(jìn)行檢查,若緩存滿了則刪除尾部元素。
import java.util.HashMap; /** * 使用cache和鏈表實(shí)現(xiàn)緩存 */ public class LRU2{ private final int MAX_CACHE_SIZE; private Entry head; private Entry tail; private HashMap > cache; public LRU2(int cacheSize) { MAX_CACHE_SIZE = cacheSize; cache = new HashMap<>(); } public void put(K key, V value) { Entry entry = getEntry(key); if (entry == null) { if (cache.size() >= MAX_CACHE_SIZE) { cache.remove(tail.key); removeTail(); } } entry = new Entry<>(); entry.key = key; entry.value = value; moveToHead(entry); cache.put(key, entry); } public V get(K key) { Entry entry = getEntry(key); if (entry == null) { return null; } moveToHead(entry); return entry.value; } public void remove(K key) { Entry entry = getEntry(key); if (entry != null) { if (entry == head) { Entry next = head.next; head.next = null; head = next; head.pre = null; } else if (entry == tail) { Entry prev = tail.pre; tail.pre = null; tail = prev; tail.next = null; } else { entry.pre.next = entry.next; entry.next.pre = entry.pre; } cache.remove(key); } } private void removeTail() { if (tail != null) { Entry prev = tail.pre; if (prev == null) { head = null; tail = null; } else { tail.pre = null; tail = prev; tail.next = null; } } } private void moveToHead(Entry entry) { if (entry == head) { return; } if (entry.pre != null) { entry.pre.next = entry.next; } if (entry.next != null) { entry.next.pre = entry.pre; } if (entry == tail) { Entry prev = entry.pre; if (prev != null) { tail.pre = null; tail = prev; tail.next = null; } } if (head == null || tail == null) { head = tail = entry; return; } entry.next = head; head.pre = entry; entry.pre = null; head = entry; } private Entry getEntry(K key) { return cache.get(key); } private static class Entry { Entry pre; Entry next; K key; V value; } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); Entry entry = head; while (entry != null) { stringBuilder.append(String.format("%s:%s ", entry.key, entry.value)); entry = entry.next; } return stringBuilder.toString(); } public static void main(String[] args) { LRU2 lru2 = new LRU2<>(5); lru2.put(1, 1); System.out.println(lru2); lru2.put(2, 2); System.out.println(lru2); lru2.put(3, 3); System.out.println(lru2); lru2.get(1); System.out.println(lru2); lru2.put(4, 4); lru2.put(5, 5); lru2.put(6, 6); System.out.println(lru2); } }
運(yùn)行結(jié)果:
FIFO
FIFO就是先進(jìn)先出,可以使用LinkedHashMap進(jìn)行實(shí)現(xiàn)。
當(dāng)?shù)谌齻€(gè)參數(shù)傳入為false或者是默認(rèn)的時(shí)候,就可以實(shí)現(xiàn)按插入順序排序,就可以實(shí)現(xiàn)FIFO緩存了。
/** * Constructs an empty LinkedHashMap instance with the * specified initial capacity, load factor and ordering mode. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @param accessOrder the ordering mode - true for * access-order, false for insertion-order * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
實(shí)現(xiàn)代碼跟上述使用LinkedHashMap實(shí)現(xiàn)LRU的代碼基本一致,主要就是構(gòu)造函數(shù)的傳值有些不同。
import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class LRU1{ private final int MAX_CACHE_SIZE; private final float DEFAULT_LOAD_FACTORY = 0.75f; LinkedHashMap map; public LRU1(int cacheSize) { MAX_CACHE_SIZE = cacheSize; int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1; /* * 第三個(gè)參數(shù)設(shè)置為true,代表linkedlist按訪問(wèn)順序排序,可作為L(zhǎng)RU緩存 * 第三個(gè)參數(shù)設(shè)置為false,代表按插入順序排序,可作為FIFO緩存 */ map = new LinkedHashMap (capacity, DEFAULT_LOAD_FACTORY, false) { @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_CACHE_SIZE; } }; } public synchronized void put(K key, V value) { map.put(key, value); } public synchronized V get(K key) { return map.get(key); } public synchronized void remove(K key) { map.remove(key); } public synchronized Set > getAll() { return map.entrySet(); } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); for (Map.Entry entry : map.entrySet()) { stringBuilder.append(String.format("%s: %s ", entry.getKey(), entry.getValue())); } return stringBuilder.toString(); } public static void main(String[] args) { LRU1 lru1 = new LRU1<>(5); lru1.put(1, 1); lru1.put(2, 2); lru1.put(3, 3); System.out.println(lru1); lru1.get(1); System.out.println(lru1); lru1.put(4, 4); lru1.put(5, 5); lru1.put(6, 6); System.out.println(lru1); } }
運(yùn)行結(jié)果:
以上就是使用Java實(shí)現(xiàn)這兩種緩存的方式,從中可以看出,LinkedHashMap實(shí)現(xiàn)緩存較為容易,因?yàn)榈讓雍瘮?shù)對(duì)此已經(jīng)有了支持,自己編寫鏈表實(shí)現(xiàn)LRU緩存也是借鑒了LinkedHashMap中實(shí)現(xiàn)的思想。在Java中不只是這兩種數(shù)據(jù)結(jié)構(gòu)可以實(shí)現(xiàn)緩存,比如ConcurrentHashMap、WeakHashMap在某些場(chǎng)景下也是可以作為緩存的,到底用哪一種數(shù)據(jù)結(jié)構(gòu)主要是看場(chǎng)景再進(jìn)行選擇,但是很多思想都是可以通用的。
關(guān)于Java緩存使用如何實(shí)現(xiàn)的就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。