📄 leastrecentlyusedcache.java
字号:
/*
* Licensed under the X license (see http://www.x.org/terms.htm)
*/
package org.ofbiz.minerva.pool.cache;
import java.io.PrintWriter;
import java.util.*;
/**
* A Least Recently Used cache implementation. The object in the
* cache that was least recently used is dropped when the cache is
* full and a new request comes in. The implementation uses a linked
* list (to track recentness) and a HashMap (to navigate quickly).
* @author Aaron Mulder ammulder@alumni.princeton.edu
*/
public class LeastRecentlyUsedCache implements ObjectCache {
private Object lock = new Object();
private HashMap keyMap = new HashMap();
private PrintWriter log = null;
private Node mostRecentNode, leastRecentNode;
private int size;
private int maxSize;
private CachedObjectFactory factory;
public LeastRecentlyUsedCache(CachedObjectFactory factory, int maxSize) {
this.factory = factory;
this.maxSize = maxSize;
}
public Object getObject(Object key) {
return getObject(key, false);
}
public Object useObject(Object key) {
return getObject(key, true);
}
public void returnObject(Object key, Object value) {
Object pooled = keyMap.get(key);
Object source = factory.translateObject(value);
if (pooled == null) {
factory.deleteObject(source);
} else if (pooled instanceof Node) {
Node node = (Node) pooled;
if (node.data == source) {
try {
node.setUsed(false);
} catch (ConcurrentModificationException e) {
}
} else {
factory.deleteObject(source);
}
} else if (pooled instanceof LinkedList) {
for (Iterator it = ((LinkedList) pooled).iterator(); it.hasNext();) {
Node node = (Node) it.next();
if (node.data == source) {
try {
node.setUsed(false);
} catch (ConcurrentModificationException e) {
}
return;
}
}
factory.deleteObject(source);
} else
throw new Error("LRU Cache Assertion Failure: Wrong class '" + pooled.getClass().getName() + "' in keyMap!");
}
public void removeObjects(Object key) {
synchronized (lock) {
Object value = keyMap.get(key);
if (value == null) {
return;
} else if (value instanceof Node) {
removeNode((Node) value);
} else if (value instanceof LinkedList) {
LinkedList list = (LinkedList) value;
int max = list.size();
for (int i = 0; i < max; i++) {
removeNode((Node) list.get(0));
}
}
}
}
public void setSize(int maxSize) {
this.maxSize = maxSize;
checkMaxSize();
}
public void close() {
synchronized (lock) {
Node current = leastRecentNode;
Node next = current == null ? null : current.moreRecent;
while (current != null) {
removeNode(current);
current = next;
next = current == null ? null : current.moreRecent;
}
}
}
private Object getObject(Object key, boolean use) {
Node node = null;
try {
Object value = keyMap.get(key);
if (value == null) {
node = addObject(key);
} else if (value instanceof Node) {
node = (Node) value;
if (!node.used) {
makeMostRecent(node);
} else {
node = addObject(key);
}
} else if (value instanceof LinkedList) {
for (Iterator it = ((LinkedList) value).iterator(); it.hasNext();) {
node = (Node) it.next();
if (!node.used) {
makeMostRecent(node);
break;
} else {
node = null;
}
}
if (node == null) {
node = addObject(key);
}
} else
throw new Error("LRU Cache Assertion Failure: Wrong class '" + value.getClass().getName() + "' in keyMap!");
if (use) {
node.setUsed(true);
}
} catch (ConcurrentModificationException e) {
return getObject(key, use);
}
return factory.prepareObject(node.data);
}
private Node addObject(Object key) {
Object data;
try {
data = factory.createObject(key);
} catch (Exception e) {
if (log != null)
e.printStackTrace(log);
return null;
}
Node result;
synchronized (lock) {
if (mostRecentNode == null) {
result = new Node(null, null, data);
mostRecentNode = result;
leastRecentNode = result;
} else {
result = new Node(mostRecentNode, null, data);
mostRecentNode.moreRecent = result;
mostRecentNode = result;
}
++size;
checkMaxSize();
}
// Put the key/value(s) and node/key in the map
Object value = keyMap.get(key);
if (value == null) {
keyMap.put(key, result);
} else if (value instanceof LinkedList) {
((LinkedList) value).add(result);
} else if (value instanceof Node) {
LinkedList list = new LinkedList();
list.add(value);
list.add(result);
keyMap.put(key, list);
} else
throw new Error("LRU Cache Assertion Failure: Wrong class '" + value.getClass().getName() + "' in keyMap!");
keyMap.put(result, key);
return result;
}
private void checkMaxSize() {
if (maxSize <= 0)
return;
while (size > maxSize) {
Node drop = leastRecentNode;
leastRecentNode = drop.moreRecent;
leastRecentNode.lessRecent = null;
--size;
removeNode(drop);
}
}
private void makeMostRecent(Node node) {
synchronized (lock) {
if (node.moreRecent == null) {
if (mostRecentNode != node)
throw new ConcurrentModificationException();
return;
}
// Prepare surrounding nodes
Node previous = node.moreRecent;
Node next = node.lessRecent;
previous.lessRecent = next;
if (next == null) {
leastRecentNode = previous;
} else {
next.moreRecent = previous;
}
// Prepare node and new 2nd most recent
node.moreRecent = null;
node.lessRecent = mostRecentNode;
mostRecentNode.moreRecent = node;
mostRecentNode = node;
}
}
// This should be called while holding the lock
private void removeNode(Node node) {
boolean used = node.used;
if (!used) {
node.used = true;
}
Object key = keyMap.remove(node);
Object value = keyMap.get(key);
if (value instanceof Node) {
keyMap.remove(key);
} else if (value instanceof LinkedList) {
LinkedList list = (LinkedList) value;
Iterator it = list.iterator();
while (it.hasNext()) {
Node current = (Node) it.next();
if (current == node) {
it.remove();
break;
}
}
if (list.size() == 1) {
keyMap.put(key, list.get(0));
}
} else
throw new Error("LRU Cache Assertion Failure: Wrong class '" + value.getClass().getName() + "' in keyMap!");
if (!used) {
factory.deleteObject(node.data);
}
node.moreRecent = null;
node.lessRecent = null;
}
private class Node {
Node lessRecent;
Node moreRecent;
Object data;
boolean used = false;
public Node(Node lessRecent, Node moreRecent, Object data) {
this(lessRecent, moreRecent, data, false);
}
public Node(Node lessRecent, Node moreRecent, Object data, boolean used) {
this.lessRecent = lessRecent;
this.moreRecent = moreRecent;
this.data = data;
this.used = used;
}
public synchronized void setUsed(boolean used) {
if (this.used == used)
throw new ConcurrentModificationException();
this.used = used;
}
}
/* This is for testing only
private void printList() {
System.out.print("Fwd Nodes:");
Node node = mostRecentNode;
for(int i=0; i<size; i++) {
System.out.print(" "+node.data);
node = node.lessRecent;
}
System.out.println();
Stack stack = new Stack();
node = leastRecentNode;
for(int i=0; i<size; i++) {
stack.push(node.data);
node = node.moreRecent;
}
System.out.print("Rev Nodes:");
while(!stack.isEmpty())
System.out.print(" "+stack.pop());
System.out.println();
}
public static void main(String args[]) {
try {
java.io.BufferedReader in = new java.io.BufferedReader(new java.io.InputStreamReader(System.in));
LeastRecentlyUsedCache cache = new LeastRecentlyUsedCache(new CachedObjectFactory() {
public Object createObject(Object identifier) {
return "v"+identifier;
}
public void deleteObject(Object object) {
System.out.println("Deleting Object "+object);
}
}, 5);
* *** THIS TESTS getObject ****
while(true) {
cache.printList();
System.out.print("> ");
System.out.flush();
String key = in.readLine();
if(key.equalsIgnoreCase("quit") || key.equalsIgnoreCase("exit"))
return;
Object value = cache.getObject(key);
System.out.println("Got Value: "+value);
}
* ********* *
**** THIS TESTS useObject AND returnObject ****
Object lastKey = null, last = null;
while(true) {
cache.printList();
System.out.print("> ");
System.out.flush();
String key = in.readLine();
if(key.equalsIgnoreCase("quit") || key.equalsIgnoreCase("exit"))
return;
Object value = cache.useObject(key);
if(last != null) {
cache.returnObject(lastKey, last);
}
lastKey = key;
last = value;
System.out.println("Got Value: "+value);
}
********** *
} catch(Throwable e) {
e.printStackTrace();
}
}
*/
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -