1. 程式人生 > >Java實現緩存(LRU,FIFO)

Java實現緩存(LRU,FIFO)

tro 減少 需求 返回 city 存在 move sed 未使用

吹吹牛逼,曬曬太陽。不如來寫點東西,哈哈哈哈哈。。。。今天來說說,如何用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)