LinkedHashMap是一个现成的LRU实现。但其缺乏并发机制,并且没有实现满载删除条件(removeEldestEntry),因此本文认为LinkedHashMap是预留扩展的LRU类。
本文将继承该类进一步实现LRU缓存。
首先,并发使用可重入锁。对于封读锁后是否可写,封写锁后是否可读的问题,这是一个封锁策略。一般而言,读后,若写,会产生脏数据。写后,若读则没有问题。
可重入锁之读写锁,封锁顺序测试:
package lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class LockTest1{
final static ReadWriteLock rwlock = new ReentrantReadWriteLock();
final static Lock readLock = rwlock.readLock();
final static Lock writeLock = rwlock.writeLock();
public static void main(String[] args) throws InterruptedException {
System.out.println("test readlock-writelock");
lockR2W();
System.out.println("test writelock-readlock");
lockW2R();
System.out.println("test readlock-readlock");
lockRR();
System.out.println("test writelock-writelock");
lockWW();
}
private static void lockWW() {
writeLock.lock();
System.out.println("w lock.");
try {
if (writeLock.tryLock(1, TimeUnit.SECONDS))
System.out.println("w lock.");
else
System.out.println("when w lock, cannot w lock again.");
} catch (InterruptedException e0) {
e0.printStackTrace();
}
try {
writeLock.unlock();
System.out.println("w unlock.");
writeLock.unlock();
System.out.println("w unlock.");
} catch (Exception e) {
//ignore;
}
}
private static void lockRR() {
readLock.lock();
System.out.println("r lock.");
readLock.lock();
System.out.println("r lock.");
readLock.unlock();
System.out.println("r unlock.");
readLock.unlock();
System.out.println("r unlock.");
}
private static void lockW2R() throws InterruptedException {
writeLock.lock();
System.out.println("w lock.");
if (readLock.tryLock(1, TimeUnit.SECONDS))
System.out.println("r lock.");
else
System.out.println("when w lock, cannot r lock.");
writeLock.unlock();
System.out.println("w unlock.");
readLock.unlock();
System.out.println("r unlock.");
}
private static void lockR2W() {
readLock.lock();
System.out.println("r lock.");
try {
if (writeLock.tryLock(1, TimeUnit.SECONDS))
System.out.println("w lock.");
else
System.out.println("when r lock, cannot w lock.");
} catch (InterruptedException e0) {
e0.printStackTrace();
}
try {
readLock.unlock();
System.out.println("r unlock.");
writeLock.unlock();
System.out.println("w unlock.");
} catch (Exception e) {
//ignore;
}
}
}
测试结果:
test readlock-writelock r lock. when r lock, cannot w lock. r unlock. test writelock-readlock w lock. r lock. w unlock. r unlock. test readlock-readlock r lock. r lock. r unlock. r unlock. test writelock-writelock w lock. w lock. w unlock. w unlock.
LRU的意义在于最近被访问的数据总是可以最快从缓存中读取到。因此,下面要测试最新数据的位置和缓存已满时数据的处理。
package lru;
import java.util.Iterator;
import java.util.Map.Entry;
public class LRUTest{
public static void main(String[] args) {
int maxCapacity = 5;
OjadbMap<Integer, String> lruMap = new OjadbMap<Integer, String>(maxCapacity);
for (int i = 0; i < maxCapacity; i++) {
lruMap.put(i, "data[" + i + "]");
}
lruMap.get(3);
Iterator<Entry<Integer, String>> iter = lruMap.entrySet().iterator();
while (iter.hasNext()) {
System.out.println(iter.next().getValue());
}
System.out.println();
lruMap.put(9, "data[ 9 ]");
Iterator<Entry<Integer, String>> iter1 = lruMap.entrySet().iterator();
while (iter1.hasNext()) {
System.out.println(iter1.next().getValue());
}
}
}
测试结果:
data[0]
data[1]
data[2]
data[4]
data[3] //最近访问的数据在队尾。
data[1]
data[2]
data[4]
data[3]
data[ 9 ] //最新访问的数据插入队尾(若队满,最老的数据被移除)。
本文使用的LRU缓存类:
(参考:这个类是我的一个开源数据库项目中,缓存部分的实现类。
你可以通过访问http://code.google.com/p/ojadb 查看整个项目)
/*******************************************************************************************
* oJadb is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* oJadb is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this library.
* If not, see <http://www.gnu.org/licenses/>.
*
* Author:EricHan
* Time:2008-8-28
******************************************************************************************/
package lru;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class OjadbMap<K, V> extends LinkedHashMap<K, V>{
private static final long serialVersionUID = -6970580442519842034L;
private final int maxCapacity;
private static final float DEFAULT_LOAD_FACTOR = 0.75f;
//private final Lock lock = new ReentrantLock();
private final ReadWriteLock rwlock = new ReentrantReadWriteLock();
private final Lock readLock = rwlock.readLock();
private final Lock writeLock = rwlock.writeLock();
public OjadbMap(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 {
readLock.lock();
return super.containsKey(key);
} finally {
readLock.unlock();
}
}
@Override
public V get(Object key) {
try {
readLock.lock();
return super.get(key);
} finally {
readLock.unlock();
}
}
@Override
public V put(K key, V value) {
try {
writeLock.lock();
return super.put(key, value);
} finally {
writeLock.unlock();
}
}
public int size() {
try {
readLock.lock();
return super.size();
} finally {
readLock.unlock();
}
}
public void clear() {
try {
writeLock.lock();
super.clear();
} finally {
writeLock.unlock();
}
}
public Collection<Map.Entry<K, V>> getAll() {
try {
readLock.lock();
return new ArrayList<Map.Entry<K, V>>(super.entrySet());
} finally {
readLock.unlock();
}
}
}