实现一个线程安全并且带有失效时间的LRU缓存:

    1. /**
    2. * @author KHighness
    3. * @since 2021-08-15
    4. */
    5. public class LRUCache<K, V> {
    6. /**
    7. * 最大容量
    8. */
    9. private final int maxCapacity;
    10. /**
    11. * 存储键值对
    12. */
    13. private ConcurrentHashMap<K, V> cache;
    14. /**
    15. * 存储有效键
    16. */
    17. private ConcurrentLinkedQueue<K> keys;
    18. /**
    19. * 读写锁
    20. */
    21. private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    22. private Lock writeLock = readWriteLock.writeLock();
    23. private Lock readLock = readWriteLock.readLock();
    24. private ScheduledExecutorService scheduledExecutorService;
    25. public LRUCache(int maxCapacity) {
    26. if (maxCapacity < 0) {
    27. throw new IllegalArgumentException("Illegal max capacity: " + maxCapacity);
    28. }
    29. this.maxCapacity = maxCapacity;
    30. cache = new ConcurrentHashMap<>(maxCapacity);
    31. keys = new ConcurrentLinkedQueue<>();
    32. scheduledExecutorService = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors() + 1);
    33. }
    34. /**
    35. * 放入缓存
    36. *
    37. * @param key 键
    38. * @param value 值
    39. * @param expireTime 过期时间
    40. * @param unit 时间单位
    41. * @return 如果缓存中已存在相同键,返回该键的值,否则返回插入的值
    42. */
    43. public V put(K key, V value, long expireTime, TimeUnit unit) {
    44. if (expireTime < 0) {
    45. throw new IllegalArgumentException("Illegal expire time: " + expireTime);
    46. }
    47. // 加写锁
    48. writeLock.lock();
    49. try {
    50. V val = cache.get(key);
    51. // key已经存在
    52. if (val != null) {
    53. moveLast(key);
    54. cache.put(key, value);
    55. return val;
    56. } else {
    57. // 缓存已满
    58. if (cache.size() == maxCapacity) {
    59. removeHead();
    60. }
    61. keys.add(key);
    62. cache.put(key, value);
    63. if (expireTime > 0) {
    64. removeAfterExpireTime(key, expireTime, unit);
    65. }
    66. return value;
    67. }
    68. } finally {
    69. // 释放写锁
    70. writeLock.unlock();
    71. }
    72. }
    73. /**
    74. * 获取键对应的值
    75. *
    76. * @param key 键
    77. * @return 值
    78. */
    79. public V get(K key) {
    80. // 加读锁
    81. readLock.lock();
    82. try {
    83. V val = get(key);
    84. if (val != null) {
    85. moveLast(key);
    86. return val;
    87. }
    88. return null;
    89. } finally {
    90. readLock.unlock();
    91. }
    92. }
    93. /**
    94. * 获取缓存中的键值对数量
    95. *
    96. * @return 键值对的数量
    97. */
    98. public int size() {
    99. return cache.size();
    100. }
    101. /**
    102. * 将key移动到队列尾部
    103. */
    104. private void moveLast(K key) {
    105. keys.remove(key);
    106. keys.add(key);
    107. }
    108. /**
    109. * 移除队列头部key和缓存中的键值对
    110. */
    111. private void removeHead() {
    112. K key = keys.poll();
    113. if (key != null) {
    114. cache.remove(key);
    115. }
    116. }
    117. /**
    118. * 清除过期key
    119. */
    120. private void removeAfterExpireTime(K key, long expireTime, TimeUnit unit) {
    121. scheduledExecutorService.schedule(() -> {
    122. cache.remove(key);
    123. keys.remove(key);
    124. }, expireTime, unit);
    125. }
    126. }