• 博客园logo
  • 会员
  • 周边
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • YouClaw
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

无信不立

  • 博客园
  • 联系
  • 订阅
  • 管理

公告

View Post

【数据结构和算法】FIFO, LFU, LRU缓存实现

一、LRU(Least Recently Used)-->最近最少使用的淘汰

(1)缓存接口

package com.sxf.study.interview.cache;

/**
 *
 */
public interface Cache {

    String get(String key,CallBack callBack);

}

interface CallBack {

    String query(String key);
}
View Code

(2)双向链表定义

package com.sxf.study.interview.cache;

/**
 *
 */
public class LinkedNode {

    private String key;
    private String data;
    private LinkedNode preNode;
    private LinkedNode nextNode;

    public String getData() {
        return data;
    }

    public void setData(String data) {
        this.data = data;
    }

    public LinkedNode getPreNode() {
        return preNode;
    }

    public void setPreNode(LinkedNode preNode) {
        this.preNode = preNode;
    }

    public LinkedNode getNextNode() {
        return nextNode;
    }

    public void setNextNode(LinkedNode nextNode) {
        this.nextNode = nextNode;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}
View Code

(3)并发查询相同key,防止雪崩查询

package com.sxf.study.interview.cache;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 通过CountDownLatch对线程进行控制,
 * 一次只会有一个线程进入资源访问,
 * 其他线程在等待进入资源访问的线程结果
 */
public class CacheValueFuture implements Future<String> {

    private CountDownLatch countDownLatch;

    private String key;

    private Object result;

    public CacheValueFuture(String key) {
        this.key = key;
        countDownLatch = new CountDownLatch(1);
    }

    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        return false;
    }

    @Override
    public boolean isCancelled() {
        return false;
    }

    @Override
    public boolean isDone() {
        return result != null;
    }

    @Override
    public String get() throws InterruptedException, ExecutionException {
        countDownLatch.await();
        if (result instanceof Throwable) {
            throw new ExecutionException((Throwable) result);
        }
        return (String) result;
    }

    @Override
    public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        countDownLatch.await(timeout, unit);
        if (result instanceof Throwable) {
            throw new ExecutionException((Throwable) result);
        }
        return (String) result;
    }


    public void setResult(Object result) {
        this.result = result;
        countDownLatch.countDown();
    }
}
View Code

(4)LRU cache定义

package com.sxf.study.interview.cache;


import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * LRU缓存
 */
public class LRUCache implements Cache {

    private Object o=new Object();

    /**
     * 防止缓存雪崩,对同一个key进行并发查询时,只会有1个线程进入查询
     */
    private Map<String, CacheValueFuture> cacheValueFutureHashMap = new ConcurrentHashMap<String, CacheValueFuture>();
    /**
     * 维护缓存数据的热点
     */
    private LinkedNode headLinkedNode;
    /**
     * 维护缓存的非热点数据
     */
    private LinkedNode tailLinkedNode;
    /**
     * 快速查找缓存数据
     */
    private Map<String, LinkedNode> linkedNodeMap;
    /**
     * 当前缓存数据的个数
     */
    private int cacheSize;
    /**
     * 缓存数据的最大个数
     */
    private int maxCacheSize;

    public LRUCache(int maxCacheSize) {
        if (maxCacheSize <= 0) {
            throw new IllegalArgumentException("maxCacheSize <= 0");
        }
        this.maxCacheSize = maxCacheSize;
    }

    @Override
    public String get(String key, CallBack callBack) {
        LinkedNode linkedNode = linkedNodeMap.get(key);
        String result;
        if (linkedNode == null) {
            //未命中缓存,降级去查询
            CacheValueFuture cacheValueFuture = new CacheValueFuture(key);
            CacheValueFuture future=cacheValueFutureHashMap.putIfAbsent(key,cacheValueFuture);
            if(future!=null){
                //已经存在其他线程,对该key进行查询了
                try {
                    result=future.get();
                }catch (Exception e){
                    throw new RuntimeException("error");
                }
            }else{
                //第一个线程对该key进行查询,进行查询
                try {
                    result = callBack.query(key);
                    //新加入节点
                    addNode(key,result);
                    //设置结果
                    cacheValueFuture.setResult(result);
                }catch (Exception e){
                    cacheValueFuture.setResult(e);
                    throw new RuntimeException("query error");
                }finally {
                    cacheValueFutureHashMap.remove(key);
                }
            }

        } else {
            //调整链表位置
            adjustNode(linkedNode);
            result=linkedNode.getData();
        }
        return result;
    }

    /**
     * 向链表中加入节点
     * @param key
     * @param value
     */
    private void addNode(String key,String value){
        LinkedNode linkedNode=new LinkedNode();
        linkedNode.setData(value);
        synchronized (o){
            //链表节点数+1
            cacheSize++;
            linkedNodeMap.put(key,linkedNode);
            //如果头节点为空,则代表链表为空
            if(this.headLinkedNode==null){
                this.headLinkedNode=linkedNode;
                this.tailLinkedNode=linkedNode;
                return;
            }

            //链表不为空,则新节点添加至链表的头节点
            this.headLinkedNode.setPreNode(linkedNode);
            linkedNode.setNextNode(this.headLinkedNode);
            this.headLinkedNode=linkedNode;

            //链表的个数超限,删除尾巴节点
            if(cacheSize>maxCacheSize) {
                LinkedNode temp=this.tailLinkedNode.getPreNode();
                tailLinkedNode.setPreNode(null);
                temp.setNextNode(null);
                String tailKey=this.tailLinkedNode.getKey();
                this.tailLinkedNode=temp;
                linkedNodeMap.remove(tailKey);
            }
        }
    }

    /**
     * 调整链表
     * @param linkedNode
     */
    private void adjustNode(LinkedNode linkedNode){
        synchronized (o){
            //当前节点为头节点,不用调整
            if(linkedNode==this.headLinkedNode){
                return ;
            }

            //当前节点为尾巴节点,需要进行调整
            if(linkedNode==this.tailLinkedNode){
                //断开尾巴节点,并设置新的尾巴节点
                LinkedNode newTailNode=this.tailLinkedNode.getPreNode();
                newTailNode.setNextNode(null);
                this.tailLinkedNode.setPreNode(null);
                LinkedNode newHeadNode=this.tailLinkedNode;
                this.tailLinkedNode=newTailNode;
                //设置新的头节点
                this.headLinkedNode.setPreNode(newHeadNode);
                newHeadNode.setNextNode(this.headLinkedNode);
                this.headLinkedNode=newHeadNode;
                return;
            }

            //当前节点为中间节点,进行调整
            //将当前节点从链表中摘除,上节点和下节点相连
            LinkedNode preNode=linkedNode.getPreNode();
            LinkedNode nextNode=linkedNode.getNextNode();
            preNode.setNextNode(nextNode);
            nextNode.setPreNode(preNode);
            //将当前节点设置为头节点
            this.headLinkedNode.setPreNode(linkedNode);
            linkedNode.setNextNode(this.headLinkedNode);
            this.headLinkedNode=linkedNode;
        }

    }
}
View Code

 

二、LFU(Least Frequently Used)---> 最少使用的淘汰

三、FIFO(First In First Out)--->先进先出     

posted on 2020-01-31 19:35  无信不立  阅读(190)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2026
浙公网安备 33010602011771号 浙ICP备2021040463号-3