私はLRU CAcheをjavaで実装しました。それは完全に動作します。私は、既存の要素を高速に取得するためのhashMapとノードの順序を保持するためのDoubleLinkedListという2つのデータ構造を使用しました。しかし、実装に効率的な同時実行メカニズムを提供する方法が混乱していますか?私はコンセプションをロックすることから始めましたが、執筆との同期なしで速い読みを保証したいと思います。私はこれをすることができないように見えるので、私はここにこだわっています。LRUキャッシュの同時バージョン
私はLRU実装のために同時キャッシュを使用して、キャッシュ全体で気まぐれなロックを避ける方法を教えてください。私たちはHashMapのとDoubleLinkedListを使用する場合、実装キャッシュの古典的なアプローチで、同時実行と一致することは不可能であることがわかり@BenManesからのリンクによると
public class LRUCacheImpl implements LRUCache {
private final Map<Integer, Node> cacheMap = new ConcurrentHashMap<>();
private final DoubleLinkedList nodeList;
private final int allowedCapacity;
public LRUCacheImpl(int allowedCapacity) {
this.allowedCapacity = allowedCapacity;
nodeList = new DoubleLinkedListImpl(allowedCapacity);
}
@Override
public Node getElement(int value) {
Node toReturn = cacheMap.get(value);
if(toReturn != null){
nodeList.moveExistingToHead(toReturn);
toReturn = new Node(toReturn.getValue());
}
else{
synchronized (this) {
if (allowedCapacity == nodeList.getCurrentSize()) {
cacheMap.remove(nodeList.getTail().getValue());
}
toReturn = new Node(value);
nodeList.addNewAsHead(toReturn);
cacheMap.put(value, toReturn);
}
}
return new Node(toReturn.getValue());
}
List<Node> getCacheState() {
return nodeList.getAllElements();
}
}
public class DoubleLinkedListImpl implements DoubleLinkedList {
private Node head;
private Node tail;
private int currentSize;
private final int allowedCapacity;
public DoubleLinkedListImpl(int allowedCapacity) {
this.currentSize = 0;
this.allowedCapacity = allowedCapacity;
}
@Override
public synchronized int getCurrentSize() {
return currentSize;
}
@Override
public synchronized Node getTail() {
return tail;
}
@Override
public void moveExistingToHead(Node element) {
if(element != null && element != head) {
synchronized (this) {
if(element != null && element != head) {
Node prev = element.getPrev();
Node next = element.getNext();
prev.setNext(next);
if (next != null) {
next.setPrev(prev);
} else {
tail = prev;
}
attachAsHead(element);
}
}
}
}
@Override
public synchronized void addNewAsHead(Node element) {
if(currentSize == 0){
head = tail = element;
currentSize = 1;
}
else if(currentSize < allowedCapacity){
currentSize++;
attachAsHead(element);
}
else{
evictTail();
attachAsHead(element);
}
}
private synchronized void attachAsHead(Node element) {
element.setPrev(null);
element.setNext(head);
head.setPrev(element);
head = element;
}
@Override
public synchronized List<Node> getAllElements() {
List<Node> nodes = new LinkedList<>();
Node tmp = head;
while(tmp != null){
nodes.add(new Node(tmp.getValue()));
tmp = tmp.getNext();
}
return nodes;
}
private synchronized void evictTail(){
tail = tail.getPrev();
tail.setNext(null);
currentSize--;
}
}
public class Node {
private int value;
private Node prev;
private Node next;
// getters and setters ommited
}
単純ではありません。特別な「更新中の」オブジェクトでCASにアクセスし、値を取得するために読んでから、値を持つCASを特別なオブジェクトに追加する必要があります。 –
詳細を教えてください。 CASとは何ですか? – Viper
'AtomicReference.compareAndSet'は一例です。 'ConcurrentHashMap'には同等のものがあります。 –