Я реализовал LRU CAche в java. Он работает отлично. Я использовал две структуры данных: hashMap для быстрого извлечения существующих элементов и DoubleLinkedList для поддержания порядка узлов. Однако я смущен тем, как я могу обеспечить эффективный механизм параллелизма для моей реализации? Я начал с концепции блокировки, но хотел бы обеспечить быстрое чтение без синхронизации с письмом, и я застрял здесь, потому что похоже, что я не могу этого сделать.Параллельная версия кэша LRU
Не могли бы вы посоветовать мне, как я могу доказать параллелизм для моей реализации LRU, избегая неэлегантной блокировки всего кеша? Ниже мой код:
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» есть эквивалент. –