一、介绍
1.1 什么是缓存
- 缓存就是数据交换的缓冲区,当某一硬件要读取数据时,会首先从缓存中查询数据,有则直接执行,不存在时再从磁盘中读取。由于缓存的数据比磁盘快的多,所以缓存的作用就是帮助硬件更快的运行。
- 缓存往往使用的是RAM(断电即掉的非永久存储),所以在用完以后还是会把文件送到硬盘等存储器中永久存储。电脑中最大的缓存就是内存条。
- 高速缓存是用来协调CPU与主存之间存取速度的差异而设置的。一般CPU工作速度高,但内存的工作速度相对较低,为了解决这个问题,通常使用高速缓存,高速缓存的存取速度介于CPU与主存之间。系统将一些CPU在最近几个时间段经常访问的内容存在高速缓存,这样就在一定程度上缓解了由于主存速度低造成的CPU“停工待料”的情况。
1.2 缓存的作用
在不同的场景下,缓存的作用是不同的:
- 操作系统磁盘缓存:减少磁盘机械操作
- 数据库缓存:减少文件系统IO
- 应用程序缓存:减少对数据库的查询
- Web服务器缓存:减少应用服务器请求
- 客户端浏览器缓存:减少对网站的访问
二、常见的缓存策略
2.1 LRU
Least Recently Used,称为最近最久未使用法,这种缓存机制会每次从内存中找到最久未使用的数据然后置换出来,从而存入新的数据。
实现:
LRU使用双向链表实现,主要涉及到添加、访问、修改、删除操作。首先是添加,如果是新元素,直接放在链表头上面,其他的元素顺序往下移动;访问的话,在头节点的可以不用管,如果是在中间位置或者尾巴,就要将数据移动到头节点;修改操作也是一样,修改原值之后,再将数据移动到头部,删除的话,直接删除,其他元素顺序移动
代码如下:
import java.util.HashMap;
class Node{
Object key;
Object value;
Node pre;
Node next;
public Node(Object key,Object value){
this.key=key;
this.value=value;
}
}
class LRU<K,V>{
private int capcity;
private HashMap<K,Node> caches;
private Node first;
private Node last;
public LRU(int size){
this.capcity=size;
caches=new HashMap<K,Node>(size);
}
//添加元素
public void put(K key,V value){
Node node=caches.get(key);
if(node==null){
if(caches.size()>=capcity){
caches.remove(last.key);
removeLast();
}
node=new Node(key,value);
caches.put(key,node);
}
else{
node.value=value;
}
moveToHead(node);
}
//访问元素
public Object get(K key){
Node node=caches.get(key);
if(node==null){
return null;
}
moveToHead(node);
return node.value;
}
//删除元素
public Object remove(K key){
Node node=caches.get(key);
if(node!=null){
if(node.pre!=null){
node.pre.next=node.next;
}
if(node.next!=null){
node.next.pre=node.pre;
}
if(node==first){
first=first.next;
}
if(node==last){
last=node.pre;
}
}
return caches.remove(key);
}
public void moveToHead(Node node){
if(first==node){
return;
}
if(node.next!=null){
node.next.pre=node.pre;
}
if(node.pre!=null){
node.pre.next=node.next;
}
if(node==last){
last=last.pre;
}
if(first==null||last==null){
first=last=node;
return;
}
node.next=first;
first.pre=node;
first=node;
first.pre=null;
}
private void removeLast(){
if(last!=null){
last=last.pre;
if(last==null){
first=null;
}
else{
last.next=null;
}
}
}
@Override
public String toString(){
StringBuilder sb=new StringBuilder();
Node node=first;
while(node!=null){
sb.append(String.format("%s:%s",node.key,node.value)+" ");
node=node.next;
}
return sb.toString();
}
}
public class test{
public static void main(String[] args) {
LRU<Integer, String> lru = new LRU<Integer, String>(5);
lru.put(1, "a");
lru.put(2, "b");
lru.put(3, "c");
lru.put(4,"d");
lru.put(5,"e");
System.out.println("原始链表为:"+lru.toString());
lru.get(4);
System.out.println("获取key为4的元素之后的链表:"+lru.toString());
lru.put(6,"f");
System.out.println("新添加一个key为6之后的链表:"+lru.toString());
lru.remove(3);
System.out.println("移除key=3的之后的链表:"+lru.toString());
}
}
输出结果:
原始链表为:5:e 4:d 3:c 2:b 1:a
获取key为4的元素之后的链表:4:d 5:e 3:c 2:b 1:a
新添加一个key为6之后的链表:6:f 4:d 5:e 3:c 2:b
移除key=3的之后的链表:6:f 4:d 5:e 2:b