如何解决内部用LinkedHashMap的LRUCache缩容时的ConcurrentModificationException
解决LRUCache缩容时的ConcurrentModificationException问题
针对你遇到的并发场景下调用iter.remove()抛出异常的问题,提供几个可行的解决方案:
1. 加全局锁同步所有操作
并发修改和迭代操作互斥是最直接的解决方式,给所有涉及缓存结构修改或迭代的方法加上同一把锁:
private final Object lock = new Object(); protected void shrinkSize() { synchronized(lock) { Iterator iter = fetchIterator(); while (iter.hasNext()) { if (size() > maxSize) { iter.remove(); } else { break; } } } } // 所有修改缓存结构或调整LRU顺序的方法(如get、put、remove)都要加同一锁 public Object get(Object key) { synchronized(lock) { // 原get逻辑,包括将访问的条目移到LRU头部等操作 return value; } }
原理:锁确保迭代过程中,没有其他线程能修改缓存结构,从根本上避免并发修改导致的迭代器异常。
2. 使用支持弱一致性的并发迭代器
如果你的LRUCache底层改用支持fail-safe(弱一致性)的容器实现,这类容器的迭代器不会因为并发修改抛出ConcurrentModificationException,比如基于ConcurrentHashMap实现LRU的链表结构。此时缩容方法可以调整为:
protected void shrinkSize() { Iterator iter = fetchConcurrentIterator(); // 循环判断直到容量符合要求,因为迭代器是弱一致性的,可能看不到实时修改 while (size() > maxSize && iter.hasNext()) { iter.remove(); } }
原理:弱一致性迭代器基于容器快照或分段锁实现,允许并发修改,不会抛出异常,但要注意迭代过程中可能无法立即感知到其他线程的修改,所以需要循环判断容量直到达标。
3. 复用LRU淘汰逻辑完成缩容
LRUCache本身就有淘汰最久未使用条目的逻辑,可以直接复用这个逻辑来完成缩容,避免单独的迭代操作:
protected void shrinkSize() { synchronized(lock) { // 循环移除最久未使用的条目,直到容量达标 while (size() > maxSize) { removeLeastRecentlyUsed(); } } } // 实现移除最久未使用条目的方法 private void removeLeastRecentlyUsed() { // 根据LRU的链表结构,移除尾部节点(最久未使用) // 具体实现根据你的LRUCache结构调整 }
原理:直接利用LRU的特性定位要移除的条目,不需要遍历整个缓存,效率更高,同时加锁确保并发安全。
内容的提问来源于stack exchange,提问作者sreenivas
相关产品推荐
相关产品推荐

