標籤:form 容量 redis 編寫 擴容 串連 大於 解決 app
吹吹牛逼,晒晒太陽。不如來寫點東西,哈哈哈哈哈。。。。今天來說說,如何用java實現緩衝,這個話題很多面試的也會被問到。今天就來說說。
1.為什麼要java實現緩衝的?
由於目前軟體或網頁的並發量增加很大,大量請求直接操作資料庫,會對資料造成很大的壓力。處理大量請求和連線時間會很長。而我們知道資料庫中70%的資料是不需要修改的,那就可以引入緩衝來進行讀取,減少資料庫的壓力。
常用的緩衝有Redis和memcached,但是有時候一些小情境就可以直接使用Java實現緩衝,就可以滿足這部分服務的需求。
緩衝主要有LRU和FIFO,LRU是Least Recently Used的縮寫,即最近最久未使用,FIFO就是先進先出,
下面就使用Java來實現這兩種緩衝
一。LRU緩衝的思想
固定緩衝大小,需要給緩衝分配一個固定的 大小
每次讀取緩衝都會改變緩衝的使用時間,將緩衝存在的時間重新重新整理。
需要在緩衝滿後,將最近,最久,未使用的緩衝刪除,再添加心得緩衝。
按照上面的思想我們可以使用LikedHashMap來實現LRU緩衝
當返回true的時候,就會remove其中最久的元素,可以通過重寫這個方法來控制緩衝元素的刪除,當緩衝滿了後,就可以通過返回true刪除最久未被使用的元素,達到LRU的要求。這樣就可以滿足上述第三點要求。
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false;}
由於LinkedHashMap是為自動擴容的,當table數組中元素大於Capacity * loadFactor的時候,就會自動進行兩倍擴容。但是為了使緩衝大小固定,就需要在初始化的時候傳入容量大小和負載因子。
為了使得到達設定緩衝大小不會進行自動擴容,需要將初始化的大小進行計算再傳入,可以將初始化大小設定為(緩衝大小 / loadFactor) + 1,這樣就可以在元素數目達到緩衝大小時,也不會進行擴容了。這樣就解決了上述第一點問題。
代碼實現
package com.huojg.test.Test;import java.util.LinkedHashMap;import java.util.Map;import java.util.Set;public class LRUCache<K, V> { private final int MAX_CACHE_SIZE; private final float DEFAULT_LOAD_FACTORY = 0.75f; LinkedHashMap<K, V> map; public LRUCache(int cacheSize) { MAX_CACHE_SIZE = cacheSize; int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1; /* * 第三個參數設定為true,代表linkedlist按訪問順序排序,可作為LRU緩衝 * 第三個參數設定為false,代表按插入順序排序,可作為FIFO緩衝 */ map = new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTORY, true) { @Override protected boolean removeEldestEntry(Map.Entry<K, V> 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<Map.Entry<K, V>> getAll() { return map.entrySet(); } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); for (Map.Entry<K, V> entry : map.entrySet()) { stringBuilder.append(String.format("%s: %s ", entry.getKey(), entry.getValue())); } return stringBuilder.toString(); } public static void main(String[] args) { LRUCache<Integer, Integer> lru1 = new LRUCache<>(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); }}
結果輸出:
1: 1 2: 2 3: 3 2: 2 3: 3 1: 1 3: 3 1: 1 4: 4 5: 5 6: 6
實現了LRU緩衝的思想
FIFO
FIFO就是先進先出,可以使用LinkedHashMap進行實現。
當第三個參數傳入為false或者是預設的時候,就可以實現按插入順序排序,就可以實現FIFO緩衝了。
實現代碼跟上述使用LinkedHashMap實現LRU的代碼基本一致,主要就是建構函式的傳值有些不同。
package com.huojg.test.Test;import java.util.LinkedHashMap;import java.util.Map;import java.util.Set;public class FIFOCache<K, V> { private final int MAX_CACHE_SIZE; private final float DEFAULT_LOAD_FACTORY = 0.75f; LinkedHashMap<K, V> map; public FIFOCache(int cacheSize) { MAX_CACHE_SIZE = cacheSize; int capacity = (int)Math.ceil(MAX_CACHE_SIZE / DEFAULT_LOAD_FACTORY) + 1; /* * 第三個參數設定為true,代表linkedlist按訪問順序排序,可作為LRU緩衝 * 第三個參數設定為false,代表按插入順序排序,可作為FIFO緩衝 */ map = new LinkedHashMap<K, V>(capacity, DEFAULT_LOAD_FACTORY, false) { @Override protected boolean removeEldestEntry(Map.Entry<K, V> 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<Map.Entry<K, V>> getAll() { return map.entrySet(); } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); for (Map.Entry<K, V> entry : map.entrySet()) { stringBuilder.append(String.format("%s: %s ", entry.getKey(), entry.getValue())); } return stringBuilder.toString(); } public static void main(String[] args) { FIFOCache<Integer, Integer> lru1 = new FIFOCache<>(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); }}
結果輸出
1: 1 2: 2 3: 3 1: 1 2: 2 3: 3 2: 2 3: 3 4: 4 5: 5 6: 6
以上就是使用Java實現這兩種緩衝的方式,從中可以看出,LinkedHashMap實現緩衝較為容易,因為底層函數對此已經有了支援,自己編寫鏈表實現LRU緩衝也是借鑒了LinkedHashMap中實現的思想。在Java中不只是這兩種資料結構可以實現緩衝,比如ConcurrentHashMap、WeakHashMap在某些情境下也是可以作為緩衝的,到底用哪一種資料結構主要是看情境再進行選擇,但是很多思想都是可以通用的。
Java實現緩衝(LRU,FIFO)