lru缓存 线程安全

6 阅读1分钟

一开始哨兵节点 dummy 的 prev 和 next 都指向 dummy。随着节点的插入,dummy 的 next 指向链表的第一个节点(最上面的书),prev 指向链表的最后一个节点(最下面的书)。

package com.yyb.juc.bagu;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;

public class Lru_juc<K, V> {
    private final int capacity;
    private final Node<K, V> dummy;
    private final ReentrantLock lock = new ReentrantLock();
    private final Map<K, Node<K, V>> cache = new HashMap<>();
    private int size = 0;

    private static class Node<K, V> {
        K key;
        V value;
        Node<K, V> next;
        Node<K, V> prev;

        Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    public Lru_juc(int capacity) {
        if (capacity <= 0) {
            throw new IllegalArgumentException("缓存容量必须大于0: " + capacity);
        }
        this.capacity = capacity;
        dummy = new Node<>(null, null);
        dummy.next = dummy;
        dummy.prev = dummy;
    }

    public V get(K key) {
        lock.lock();
        try {
            Node<K, V> node = cache.get(key);
            if (node == null) {
                return null;
            }
            moveToHead(node);
            return node.value;
        } finally {
            lock.unlock();
        }
    }

    public void put(K key, V value) {
        lock.lock();
        try {
            Node<K, V> node = cache.get(key);
            if (node != null) {
                node.value = value;
                moveToHead(node);
            } else {
                if (size >= capacity) {
                    Node<K, V> tail = dummy.prev;
                    cache.remove(tail.key);
                    removeNode(tail);
                    size--;
                }
                node = new Node<>(key, value);
                addToHead(node);
                cache.put(key, node);
                size++;
            }
        } finally {
            lock.unlock();
        }
    }

    public int size() {
        lock.lock();
        try {
            return size;
        } finally {
            lock.unlock();
        }
    }

    public void clear() {
        lock.lock();
        try {
            cache.clear();
            dummy.next = dummy;
            dummy.prev = dummy;
            size = 0;
        } finally {
            lock.unlock();
        }
    }

    private void moveToHead(Node<K, V> node) {
        removeNode(node);
        addToHead(node);
    }

    private void removeNode(Node<K, V> node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void addToHead(Node<K, V> node) {
        node.prev = dummy;
        node.next = dummy.next;
        dummy.next.prev = node;
        dummy.next = node;
    }
}