Java 8实现缓存服务(维护两种淘汰策略(FIFO LRU)的并发高速缓存)

场景:请使用Java 8实现缓存服务。
该服务可以提供中等级别的缓存同时访问。


要实施的访问操作包括:
           1. get(key) - 此操作将获取列表中键的值。如果密钥不存在,则返回-1。
           2. set(key,value) - 如果密钥不存在,此操作将在列表中插入新的键/值,否则不执行任何操作。
           3.缓存的大小可通过属性文件进行配置


要求:1.实现具有最近最少使用(LRU)策略的通用基于内存的缓存作为列表
           2.实现访问操作的并发性。


主要代码实现:

package dpnice.cache;

import dpnice.common.GlobalConstants;
import dpnice.util.ConfigurationUtils;
import org.apache.log4j.Logger;

import java.util.concurrent.*;


/**
 * 维护两种淘汰策略(FIFO LRU)的高速缓存
 *
 * @author DPn!ce   date 2018 07 12 上午 10:19
 */
public class FastCache<K, V> {
    private final static Logger log = Logger.getLogger(FastCache.class);
    /**
     * 最大缓存大小
     * 单位 byte
     */
    private Long cacheSize;
    /**
     * 初始化内存大小
     */
    private Long initCacheSize;
    /**
     * FIFO并发缓存容器
     */
    private ConcurrentHashMap<Object, CacheNode> nodesFIFO;
    /**
     * FIFO并发缓存容器大小
     */
    private int nodesFIFOCacheSize = 10000;
    /**
     * FIFO链表头
     */
    private CacheNode firstFIFO;
    /**
     * FIFO链表尾
     */
    private CacheNode lastFIFO;
    /**
     * LRU并发缓存容器
     */
    private ConcurrentHashMap<Object, CacheNode> nodesLRU;
    /**
     * LRU链表头
     */
    private CacheNode firstLRU;
    /**
     * LRU链表尾
     */
    private CacheNode lastLRU;


    public FastCache() {
        setCacheSize();
        initCacheSize = getCurrentSize();
        nodesLRU = new ConcurrentHashMap<>();
        nodesFIFO = new ConcurrentHashMap<>(nodesFIFOCacheSize);
    }

    /**
     * 添加缓存
     *
     * @param key   键
     * @param value 对应值
     */
    public synchronized void put(K key, V value) {
        CacheNode cacheNode = null;
        try {
            cacheNode = getCacheNode(key);
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
        if (cacheNode == null) {
            //FIFO缓存容器是否已经超过大小
            if (nodesFIFO.size() > nodesFIFOCacheSize) {
                if (firstFIFO != null) {
                    //先进先出
                    nodesFIFO.remove(firstFIFO.getKey());
                    firstFIFO.getNext().setFront(null);
                    firstFIFO = firstFIFO.getNext();
                }
            }
            cacheNode = new CacheNode();
            cacheNode.setKey(key);
            cacheNode.setValue(value);
            nodesFIFO.put(key, cacheNode);
            if (lastFIFO != null) {
                lastFIFO.setNext(cacheNode);
                cacheNode.setFront(lastFIFO);
            }
            lastFIFO = cacheNode;
            if (firstFIFO == null) {
                firstFIFO = lastFIFO;
            }
        }
        //不等于空说明找到node 不进行操作
    }

    /**
     * 获取缓存中对象
     *
     * @param key 键
     * @return 对应Object
     */
    public Object get(K key) {
        CacheNode cacheNode = null;
        try {
            cacheNode = getCacheNode(key);
        } catch (ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
        return cacheNode == null ? -1 : cacheNode.getValue();
    }

    /**
     * 获取缓存中对象
     *
     * @param key 键
     * @return 对应CacheNode
     */
    private CacheNode getCacheNode(K key) throws ExecutionException, InterruptedException {

        CompletableFuture<CacheNode> cacheNodeCompletableFuture = CompletableFuture.supplyAsync(
                () -> {
                    if (firstLRU != null && firstLRU.getKey() == key) {
                        return firstLRU;
                        //本身就在第一个 不需要做移动
                    } else {
                        CacheNode node = nodesLRU.get(key);
                        if (node != null) {
                            //移动到第一个
                            moveToHead(node);
                            return node;
                        } else {
                            CacheNode node1 = nodesFIFO.get(key);
                            if (node1 != null) {
                                //从fifo队列删除 及fifo容器中删除
                                removeFIFO(node1);
                                //并添加到lru顶部 及lru链表顶部
                                putNodesLRU(node1);
                                return node1;
                            } else {
                                return null;
                            }
                        }
                    }
                });
        return cacheNodeCompletableFuture.get();
    }

    /**
     * 表示这个节点最近被使用 -> 移动到链表头
     *
     * @param nodeLRU 存放在LRU缓存容器中的node
     */
    private void moveToHead(CacheNode nodeLRU) {

        if (nodeLRU.getFront() != null) {
            //将nodeLRU的前一个的下一个指向nodeLRU的下一个
            nodeLRU.getFront().setNext(nodeLRU.getNext());
        }
        if (nodeLRU.getNext() != null) {
            //将nodeLRU的下一个的前一个指向nodeLRU前一个
            nodeLRU.getNext().setFront(nodeLRU.getFront());
        }
        if (lastLRU == nodeLRU) {
            //将尾部替换为lastLRU的上一个
            lastLRU = nodeLRU.getFront();
        }
        if (firstLRU != null) {
            nodeLRU.setNext(firstLRU);
            firstLRU.setFront(nodeLRU);
        }
        firstLRU = nodeLRU;
        //说明为链表头
        nodeLRU.setFront(null);
        if (lastLRU == null) {
            lastLRU = firstLRU;
            nodeLRU.setNext(null);
        }
    }

    /**
     * 从FIFO链表删除某一个node
     *
     * @param nodeFIFO 在FIFO中的node
     */
    private void removeFIFO(CacheNode nodeFIFO) {
        //从fifo容器中删除
        nodesFIFO.remove(nodeFIFO.getKey());
        if (lastFIFO == nodeFIFO) {
            if (lastFIFO.getFront() != null) {
                lastFIFO.getFront().setNext(null);
                lastFIFO = lastFIFO.getFront();
            } else {
                firstFIFO = null;
            }
            lastFIFO = nodeFIFO.getFront();
        } else {
            CacheNode index = lastFIFO.getFront();
            while (index != null) {
                if (index == nodeFIFO) {
                    if (index.getFront() != null) {
                        index.getFront().setNext(index.getNext());
                    } else {
                        firstFIFO = index.getNext();
                    }
                    index.getNext().setFront(index.getFront());
                    break;
                }
                //改变下标
                index = index.getFront();
            }
        }
    }

    /**
     * 第二次使用存入lru容器
     *
     * @param newNode 新的缓存node 从FIFO队列里拿出的node
     */
    private void putNodesLRU(CacheNode newNode) {
        newNode.setFront(null);
        newNode.setNext(null);
        if (firstLRU != null) {
            firstLRU.setFront(newNode);
            newNode.setNext(firstLRU);
        }
        firstLRU = newNode;
        //判断当前内存是否超过
        if (getCurrentSize() - initCacheSize > cacheSize) {
            if (lastLRU != null) {
                nodesLRU.remove(lastLRU.getKey());
                if (lastLRU.getFront() != null) {
                    lastLRU.getFront().setNext(null);
                } else {
                    //第一个也是最后一个
                    firstLRU = null;
                }
                lastLRU = lastLRU.getFront();
            }

        }
        nodesLRU.put(newNode.getKey(), newNode);
    }

    /**
     * 获得当前可用内存
     *
     * @return 可用内存
     */
    private Long getCurrentSize() {
        return Runtime.getRuntime().freeMemory();
    }

    private void setCacheSize() {
        //从配置文件中获取
        String cacheUnit = ConfigurationUtils.getString(GlobalConstants.MEMORY_CACHE_UNIT);
        Long cacheSize = Long.valueOf(ConfigurationUtils.getInteger(GlobalConstants.MEMORY_CACHE_SIZE));
        switch (cacheUnit) {
            case "gb":
                this.cacheSize = cacheSize * 1024 * 1024 * 1024;
                break;
            case "mb":
                this.cacheSize = cacheSize * 1024 * 1024;
                break;
            case "kb":
                this.cacheSize = cacheSize * 1024;
                break;
            case "GB":
                this.cacheSize = cacheSize * 1024 * 1024 * 1024;
                break;
            case "MB":
                this.cacheSize = cacheSize * 1024 * 1024;
                break;
            case "KB":
                this.cacheSize = cacheSize * 1024;
                break;
            default:
                log.error("缓存大小单位 " + cacheUnit + " 未识别,请使用kb mb gb");
                break;
        }
    }

    public void printLRUSortASC() {
        int count = 0;
        CacheNode next = firstLRU;
        while (next != null) {
            count++;
            System.out.println("LRU 第 " + count + " 个 key:" + next.getKey() + " value:" + next.getValue());
            next = next.getNext();
        }
    }

    public void printFIFOSortASC() {
        int count = 0;
        CacheNode next = firstFIFO;
        while (next != null) {
            count++;
            System.out.println("FIFO 第 " + count + " 个 key:" + next.getKey() + " value:" + next.getValue());
            next = next.getNext();
        }
    }
}

测试代码:

package dpnice.cache;

import org.junit.Test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.junit.Assert.*;

/**
 * @author DPn!ce   date 2018 07 13 下午 2:25
 */
public class FastCacheTest {
    private FastCache<String, String> fastCache = new FastCache<>();


    @Test
    public void put() throws Exception {
        fastCache.put("id1", "message:缓存的消息1");
        fastCache.put("id1", "message:缓存的消息1");
        fastCache.put("id2", "message:缓存的消息2");
        fastCache.put("id3", "message:缓存的消息3");
        fastCache.put("id4", "message:缓存的消息4");
        fastCache.put("id1", "message:缓存的消息1");
        fastCache.put("id2", "message:缓存的消息2");
        fastCache.put("id3", "message:缓存的消息3");
        fastCache.put("id4", "message:缓存的消息4");
        fastCache.put("id2", "message:缓存的消息2");
        fastCache.put("id2", "message:缓存的消息2");
//        Thread.sleep(2000L);
        System.out.println("id2 ->" + fastCache.get("id2"));
        System.out.println("id4 ->" + fastCache.get("id4"));
        System.out.println("id1 ->" + fastCache.get("id1"));
        System.out.println("id3 ->" + fastCache.get("id3"));

        fastCache.printLRUSortASC();

    }

    @Test
    public void get() throws Exception {
        System.out.println(fastCache.get("id2"));
        System.out.println(fastCache.get("id4"));
        System.out.println(fastCache.get("id1"));
        System.out.println(fastCache.get("id3"));
    }


    @Test
    public void put2() throws Exception {
        for (int i = 1; i < 5; i++) {
            fastCache.put("id" + i, "message:缓存的消息" + i);
        }
        //初始顺序: 4-3-2-1
        fastCache.printFIFOSortASC();
        //最近访问的在最顶部
        System.out.println(fastCache.get("id1"));
        System.out.println(fastCache.get("id3"));
        //预期顺序: LRU:3-1 FIFO:2-4
        fastCache.printLRUSortASC();
        fastCache.printFIFOSortASC();
    }

    @Test
    public void multithreadingTest() throws Exception {
        for (int i = 0; i < 10000; i++) {
            fastCache.put("id" + i, "message:缓存的消息" + i);
        }
       ExecutorService executor = Executors.newFixedThreadPool(10);
//        fastCache.printFIFOSortASC();
        //最近访问的在最顶部
        for (int i = 0; i < 1000000; i++) {
            executor.submit(() -> {
                Double v = Math.random() * 10000;
                String o = fastCache.get("id" + v.intValue()) + "";
//                System.out.println(o);
            });

        }
//        fastCache.printLRUSortASC();
//        fastCache.printFIFOSortASC();

    }

}

随机获取100W缓存结果:

猜你喜欢

转载自blog.csdn.net/dpnice/article/details/81069554