http://www.blogjava.net/killme2008/archive/2008/01/14/149645.html
最简单的LRU算法实现,就是利用jdk的LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可,如下所示:
如果你去看LinkedHashMap的源码可知,LRU算法是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入的内容直接放在链表头,如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置。
LRU算法还可以通过计数来实现,缓存存储的位置附带一个计数器,当命中时将计数器加1,替换时就查找计数最小的位置并替换,结合访问时间戳来实现。这种算法比较适合缓存数据量较小的场景,显然,遍历查找计数最小位置的时间复杂度为O(n)。我实现了一个,结合了访问时间戳,当最小计数大于MINI_ACESS时(这个参数的调整对命中率有较大影响),就移除最久没有被访问的项:
最简单的LRU算法实现,就是利用jdk的LinkedHashMap,覆写其中的removeEldestEntry(Map.Entry)方法即可,如下所示:
import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.Map; /** * 类说明:利用LinkedHashMap实现简单的缓存, 必须实现removeEldestEntry方法,具体参见JDK文档 * * * @param <K> * @param <V> */ public class LRULinkedHashMap<K, V> extends LinkedHashMap<K, V> { private final int maxCapacity; private static final float DEFAULT_LOAD_FACTOR = 0.75f; private final Lock lock = new ReentrantLock(); public LRULinkedHashMap(int maxCapacity) { super(maxCapacity, DEFAULT_LOAD_FACTOR, true); this.maxCapacity = maxCapacity; } @Override protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) { return size() > maxCapacity; } @Override public boolean containsKey(Object key) { try { lock.lock(); return super.containsKey(key); } finally { lock.unlock(); } } @Override public V get(Object key) { try { lock.lock(); return super.get(key); } finally { lock.unlock(); } } @Override public V put(K key, V value) { try { lock.lock(); return super.put(key, value); } finally { lock.unlock(); } } public int size() { try { lock.lock(); return super.size(); } finally { lock.unlock(); } } public void clear() { try { lock.lock(); super.clear(); } finally { lock.unlock(); } } public Collection<Map.Entry<K, V>> getAll() { try { lock.lock(); return new ArrayList<Map.Entry<K, V>>(super.entrySet()); } finally { lock.unlock(); } } }
如果你去看LinkedHashMap的源码可知,LRU算法是通过双向链表来实现,当某个位置被命中,通过调整链表的指向将该位置调整到头位置,新加入的内容直接放在链表头,如此一来,最近被命中的内容就向链表头移动,需要替换时,链表最后的位置就是最近最少使用的位置。
LRU算法还可以通过计数来实现,缓存存储的位置附带一个计数器,当命中时将计数器加1,替换时就查找计数最小的位置并替换,结合访问时间戳来实现。这种算法比较适合缓存数据量较小的场景,显然,遍历查找计数最小位置的时间复杂度为O(n)。我实现了一个,结合了访问时间戳,当最小计数大于MINI_ACESS时(这个参数的调整对命中率有较大影响),就移除最久没有被访问的项:
package net.rubyeye.codelib.util.concurrency.cache; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * *类说明:当缓存数目不多时,才用缓存计数的传统LRU算法 * @param <K> * @param <V> */ public class LRUCache<K, V> implements Serializable { private static final int DEFAULT_CAPACITY = 100; protected Map<K, ValueEntry> map; private final ReadWriteLock lock = new ReentrantReadWriteLock(); private final Lock readLock = lock.readLock(); private final Lock writeLock = lock.writeLock(); private final volatile int maxCapacity; //保持可见性 public static int MINI_ACCESS = 5; public LRUCache() { this(DEFAULT_CAPACITY); } public LRUCache(int capacity) { if (capacity <= 0) throw new RuntimeException("缓存容量不得小于0"); this.maxCapacity = capacity; this.map = new HashMap<K, ValueEntry>(maxCapacity); } public boolean ContainsKey(K key) { try { readLock.lock(); return this.map.containsKey(key); } finally { readLock.unlock(); } } public V put(K key, V value) { try { writeLock.lock(); if ((map.size() > maxCapacity - 1) && !map.containsKey(key)) { // System.out.println("开始"); Set<Map.Entry<K, ValueEntry>> entries = this.map.entrySet(); removeRencentlyLeastAccess(entries); } ValueEntry new_value = new ValueEntry(value); ValueEntry old_value = map.put(key, new_value); if (old_value != null) { new_value.count = old_value.count; return old_value.value; } else return null; } finally { writeLock.unlock(); } } /** * 移除最近最少访问 */ protected void removeRencentlyLeastAccess( Set<Map.Entry<K, ValueEntry>> entries) { // 最小使用次数 long least = 0; // 访问时间最早 long earliest = 0; K toBeRemovedByCount = null; K toBeRemovedByTime = null; Iterator<Map.Entry<K, ValueEntry>> it = entries.iterator(); if (it.hasNext()) { Map.Entry<K, ValueEntry> valueEntry = it.next(); least = valueEntry.getValue().count.get(); toBeRemovedByCount = valueEntry.getKey(); earliest = valueEntry.getValue().lastAccess.get(); toBeRemovedByTime = valueEntry.getKey(); } while (it.hasNext()) { Map.Entry<K, ValueEntry> valueEntry = it.next(); if (valueEntry.getValue().count.get() < least) { least = valueEntry.getValue().count.get(); toBeRemovedByCount = valueEntry.getKey(); } if (valueEntry.getValue().lastAccess.get() < earliest) { earliest = valueEntry.getValue().count.get(); toBeRemovedByTime = valueEntry.getKey(); } } // System.out.println("remove:" + toBeRemoved); // 如果最少使用次数大于MINI_ACCESS,那么移除访问时间最早的项(也就是最久没有被访问的项) if (least > MINI_ACCESS) { map.remove(toBeRemovedByTime); } else { map.remove(toBeRemovedByCount); } } public V get(K key) { try { readLock.lock(); V value = null; ValueEntry valueEntry = map.get(key); if (valueEntry != null) { // 更新访问时间戳 valueEntry.updateLastAccess(); // 更新访问次数 valueEntry.count.incrementAndGet(); value = valueEntry.value; } return value; } finally { readLock.unlock(); } } public void clear() { try { writeLock.lock(); map.clear(); } finally { writeLock.unlock(); } } public int size() { try { readLock.lock(); return map.size(); } finally { readLock.unlock(); } } public long getCount(K key) { try { readLock.lock(); ValueEntry valueEntry = map.get(key); if (valueEntry != null) { return valueEntry.count.get(); } return 0; } finally { readLock.unlock(); } } public Collection<Map.Entry<K, V>> getAll() { try { readLock.lock(); Set<K> keys = map.keySet(); Map<K, V> tmp = new HashMap<K, V>(); for (K key : keys) { tmp.put(key, map.get(key).value); } return new ArrayList<Map.Entry<K, V>>(tmp.entrySet()); } finally { readLock.unlock(); } } class ValueEntry implements Serializable { private V value; private AtomicLong count; private AtomicLong lastAccess; public ValueEntry(V value) { this.value = value; this.count = new AtomicLong(0); lastAccess = new AtomicLong(System.nanoTime()); } public void updateLastAccess() { this.lastAccess.set(System.nanoTime()); } } }
发表评论
-
Consistent Hashing
2010-02-09 17:12 802import java.util.Collection; ... -
防止JAVA程序重复启动的一个另类解决办法
2009-03-31 20:29 1931http://www.iteye.com/topic/3773 ... -
MappedByteBuffer内存映射
2009-03-15 21:26 2146通过把一个套接字通道(SocketChannel)注册到一个选 ... -
如何知道方法的调用者
2009-03-11 20:35 802http://www.iteye.com/topic/1317 ... -
JAVA中操作数据库方式与设计模式的应用
2009-03-11 15:02 839http://www.iteye.com/topic/1981 ... -
java调用Oracle EXP备忘
2009-03-10 11:28 1549http://www.blogjava.net/BlueDav ... -
关于java中volatile字段的ordering
2009-03-08 22:00 758多个volatile操作之间是有序的,compiler和处理 ... -
Initialize-on-demand Holder Class
2009-03-02 15:01 909public class Singleton { ... -
Interceptor的实现
2009-03-01 21:26 804public interface Action { p ... -
Quartz CronTrigger最完整配置说明
2009-02-15 15:12 998CronTrigger配置格式: 格 ... -
ClassLoader介绍
2009-01-21 13:24 806JVM在运行时会产生三个ClassLoader,Bootstr ... -
消息的发送与回调
2009-01-06 22:03 812/** * 回调接口 * @author ... -
自己编写IOC
2009-01-05 21:59 1178<?xml version="1.0&qu ... -
四则运算的中缀转后缀
2008-12-11 11:41 1618import java.math.BigDecimal ... -
一个简单的多线程、断点下载Java程序
2008-12-10 20:49 1897//这个是任务Bean public cl ... -
生产者-消费者
2008-12-02 14:05 820package debug; import java.u ...
相关推荐
在C语言中实现LRU算法,需要理解数据结构和算法的基础知识,以及如何在C语言中有效地管理内存。 首先,LRU算法的核心是数据结构的选择。通常使用双向链表来存储数据,因为双向链表允许我们快速地插入和删除元素,...
当缓存满时,LRU算法会优先淘汰最近最少使用的数据。在这个Java实现的LRU缓存中,我们可能会看到以下几个关键知识点: 1. **数据结构的选择**: LRU缓存通常基于哈希表和双向链表实现。哈希表用于快速查找,而双向...
在Java中实现LRU算法,通常会使用数据结构如HashMap或LinkedHashMap来存储页面及其访问信息。HashMap提供快速的查找操作,而LinkedHashMap则同时保持了插入顺序,这对于实现LRU至关重要,因为我们需要快速找到最近...
实验内容包括实现LRU算法的两种不同变体:计数器实现和栈实现。在计数器实现中,每个页面都有一个访问计数器,每当页面被访问时,计数器增加,淘汰时选择计数最小的页面。而在栈实现中,页面按访问顺序存储在栈中,...
现在,实现LRU算法的主要逻辑在up_cache函数中,该函数将页面访问序列walk_sort和缓存数组cache作为输入参数。 void up_cache(Cache cache[], int walk_sort[]) { int i = 0; // i 为访问序列数组的下标 int x; ...
LRU算法的实现通常依赖于数据结构,如哈希表和双向链表。在C++中,我们可以使用`std::unordered_map`来存储页面及其访问时间,同时使用`std::list`来维护页面的顺序。以下是一个简单的C++实现概述: ```cpp #...
在实现LRU算法时,通常会用到数据结构如链表和哈希表。链表用于快速定位最近使用和最久未使用的页面,而哈希表用于快速查找页面。当一个页面被访问时,它会被移到链表的头部,表示最近被使用。如果链表已满,且有新...
### LRU算法C语言实现详解 #### 一、引言 LRU(Least Recently Used,最近最少使用)算法是一种常用的数据缓存淘汰策略,在计算机科学领域应用广泛,尤其是在操作系统、数据库管理和Web服务器缓存管理等方面。本文...
在实际应用中,LRU 算法常用于缓存管理和数据库系统,以优化数据的访问效率。例如,当内存不足以存放所有数据时,LRU 可以帮助决定哪些数据应该被暂时移除,以便为更重要的数据腾出空间。由于 LRU 的高效性和直观性...
本篇文章将深入探讨LRU算法的基本原理,并通过一个具体的Node.js实现案例来展示如何利用LRU算法进行高效的缓存管理。 #### 二、LRU算法简介 LRU算法的核心思想是当缓存空间满时,优先淘汰那些最近最久未使用的数据...
通过以上介绍,我们可以看出LRU算法在Java中的实现主要依赖于HashMap和DoublyLinkedList这两个数据结构,它们结合在一起提供了高效且灵活的缓存管理能力。在实际应用中,LRU算法广泛应用于数据库的缓存系统、操作...
在本篇文章中,我们将深入探讨一个基于C语言实现的LRU算法示例,该示例通过队列的形式来管理缓存中的页面,以确保能够有效地执行淘汰策略。 #### 关键知识点 ##### 1. 数据结构定义 在给定的代码片段中,作者使用...
在缓存或者数据库管理系统中,LRU算法被广泛应用,因为它的实现简单且效果良好。 LRU算法的核心思想是:当内存满时,最近最少使用的数据会被优先淘汰。也就是说,如果一个数据最近被访问过,那么它在未来被访问的...
LRU(Least Recently Used)算法是一种常用的页面替换算法,用于管理有限的内存资源。...通过深入研究这个LRU算法的实现,你不仅可以掌握LRU算法的工作原理,还能了解到如何在实际项目中应用和优化这种缓存策略。
LRU 算法的实现简单,但命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。 4. LRU-K 算法 LRU-K 算法是为了解决 LRU 算法“缓存污染”的问题,其核心思想是将“最近使用过 1 次”的判断标准...
在数据库管理系统中,Buffer Pool(缓冲...以上内容涵盖了Buffer Pool中缓存页管理的核心概念,包括LRU算法在缓存淘汰中的应用。这些知识点对于理解和优化数据库性能至关重要,是数据库管理员和开发者必须掌握的技术。
在操作系统、数据库管理系统和缓存系统等领域,LRU算法都有广泛的应用。在这个实验设计课程中,我们将探讨LRU算法的原理和实现。 首先,我们需要理解LRU算法的核心思想。假设有一个固定大小的缓存(或内存),当新...
这里的LRUCache类维护了一个双向链表和一个哈希表,用于实现LRU算法。具体实现细节如下: - get方法:首先在哈希表中查找对应的键值对,如果不存在,则返回-1;如果存在,则将对应的节点从双向链表中删除,并将其...
LRU算法在实际应用中有着广泛的应用,例如在Web服务器缓存管理、数据库查询缓存、文件系统缓存等方面。理解并掌握LRU算法对于提升系统性能至关重要,因为它能够有效地减少因磁盘I/O导致的性能瓶颈。在C语言中实现LRU...