Java 实现 FIFO 缓存算法

Posted 龙凌云端

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 实现 FIFO 缓存算法相关的知识,希望对你有一定的参考价值。

Java 实现 FIFO 缓存算法


 

一、什么是 FIFO

FIFOFirst In, First Out)是一种常见的数据结构,也叫做先进先出队列。它的特点是先进入队列的数据最先出队。
类似于现实中排队买东西的场景,先来的人先排队,先买完商品的人先离开。

在计算机领域,FIFO常用于缓存管理、进程调度等场景中。
在缓存管理中,先进入缓存的数据将被优先保留,后进入的数据会被淘汰;在进程调度中,先进入队列等待CPU资源的进程将先被分配到CPU运行。
 

二、Java 实现 FIFO 算法

1、方案1

import java.util.LinkedList;

public class FIFOQueue<T> 
    private final LinkedList<T> queue = new LinkedList<T>();
    private final int maxSize;

    public FIFOQueue(int maxSize) 
        this.maxSize = maxSize;
    

    public void add(T item) 
        if (queue.size() >= maxSize) 
            queue.removeFirst();
        
        queue.addLast(item);
    

    public T remove() 
        return queue.removeFirst();
    

    public boolean isEmpty() 
        return queue.isEmpty();
    

    public int size() 
        return queue.size();
    

    public T peek() 
        return queue.peek();
    
上述代码使用 LinkedList 作为底层实现,定义了一个 FIFOQueue 类,支持元素添加、删除、判断队列是否为空、获取队列长度以及获取队头元素等操作。
其中,maxSize 为队列的最大容量,当队列元素数量达到最大容量时,再添加新的元素时会将最早加入的元素移除。
这里使用了 LinkedList 的 removeFirst() 方法和 addLast() 方法,保证队头元素是最早加入队列的元素。
 
使用示例:
public class Test 
    public static void main(String[] args) 
        FIFOQueue<String> queue = new FIFOQueue<>(5);
        queue.add("a");
        queue.add("b");
        queue.add("c");
        System.out.println(queue.remove()); // 输出:a
        System.out.println(queue.peek()); // 输出:b
        System.out.println(queue.size()); // 输出:2
        queue.add("d");
        queue.add("e");
        queue.add("f");
        System.out.println(queue.remove()); // 输出:b
    
上述代码演示了如何创建一个FIFOQueue对象,添加、删除元素以及获取队列状态信息等操作。
其中,先添加的元素"a"最先出队,后添加的元素"f"最后出队。
 

2、方案2

import java.util.LinkedList;

public class FIFOCache<K, V> 
    private final int capacity;
    private final LinkedList<K> keyList;
    private final Map<K, V> cacheMap;

    public FIFOCache(int capacity) 
        this.capacity = capacity;
        this.keyList = new LinkedList<>();
        this.cacheMap = new HashMap<>();
    

    public synchronized void put(K key, V value) 
        if (keyList.size() == capacity) 
            K removedKey = keyList.removeFirst();
            cacheMap.remove(removedKey);
        
        keyList.addLast(key);
        cacheMap.put(key, value);
    

    public synchronized V get(K key) 
        return cacheMap.get(key);
    
在上述代码中,使用了 Java 的 LinkedList 类来维护一个按照顺序的 key 列表,并通过 Java 的 Map 接口实现了缓存的 key-value 存储。
当缓存满时,删除队列中最早的元素,并从缓存中删除相应的键值对。当获取缓存值时,直接从缓存中获取相应的值。

需要注意的是,在多线程环境下,需要加上 synchronized 关键字来保证线程安全。
同时,由于 LinkedList 和 HashMap 的实现不是线程安全的,因此在多线程环境下,还需要使用线程安全的集合类来代替它们。
 
 

详解三种缓存过期策略LFU,FIFO,LRU(附带实现代码)

  在学操作系统的时候,就会接触到缓存调度算法,缓存页面调度算法:先分配一定的页面空间,使用页面的时候首先去查询空间是否有该页面的缓存,如果有的话直接拿出来,如果没有的话先查询,如果页面空间没有满的时候,使用新页面的时候,就释放旧的页面空间,把新页面缓存起来,以便下次使用同样的页面的时候方便调用。

缓存调度流程图

缓存机制就是上面所说的那样,但是实现的过程以及淘汰旧页面的机制不同,所以会有不同缓存调度方法,就常见的就是FIFO,LRU,LFU缓存过期策略。

1.FIFO(First In First out):先见先出,淘汰最先近来的页面,新进来的页面最迟被淘汰,完全符合队列。

2.LRU(Least recently used):最近最少使用,淘汰最近不使用的页面

3.LFU(Least frequently used): 最近使用次数最少, 淘汰使用次数最少的页面

下面详细解释三种算法是怎么实现的。下面解释转自http://blog.csdn.net/yangpl_tale/article/details/44998423

 一、FIFO

按照“先进先出(First In,First Out)”的原理淘汰数据,正好符合队列的特性,数据结构上使用队列Queue来实现。

如下图:

1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动;

2. 淘汰FIFO队列头部的数据;

 

二、LRU

(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。

最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:

1. 新数据插入到链表头部;

2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;

3. 当链表满的时候,将链表尾部的数据丢弃。

 

三、LFU

(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。

LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。

具体实现如下:

 

1. 新加入数据插入到队列尾部(因为引用计数为1);

2. 队列中的数据被访问后,引用计数增加,队列重新排序;

3. 当需要淘汰数据时,将已经排序的列表最后的数据块删除。

 前面两种算法实现并不算难,LFU实现起来会有点麻烦,下面是我自己考虑的淘汰实现思路,来了页面的时候会发生如下情况

1.判断是否有旧的页面存在,没有则淘汰尾部元素,增加新的元素。

2.有旧页面的存在,查询旧页面的位置,然后把旧页面的元素一直上移到同等次数的页面的上一个元素。

比如说元素(a,1)代表页面为a,调用次数为1,那么在缓存里面集合为{ (f,4), (e,2), (d,2),  (c,2), (b,2), (a,1) },这时候调用c页面,只需要用折半查询找到c元素的位置,把c元素存储起来,把c元素到e元素之间的元素(不包含c元素)全部后移一位,然后把c元素放到原来e元素的位置即可。

下面附上三种算法的下载地址:

下载地址

 

以上是关于Java 实现 FIFO 缓存算法的主要内容,如果未能解决你的问题,请参考以下文章

缓存算法(FIFO LRULFU三种算法的区别)

昨天面试被问到的 缓存淘汰算法FIFOLRULFU及Java实现

Java实现缓存(LRU,FIFO)

详解三种缓存过期策略LFU,FIFO,LRU(附带实现代码)

缓存算法(页面置换算法)-FIFOLFULRU

缓存系统失效算法与应用