java - 随机 java.util.ConcurrentModificationException : null

标签 java spring java-stream concurrentmodification

我有一个每 5 分钟运行一次并从缓存中删除文件的方法

private HashMap<String, CachedFile> cache = new HashMap<>();

@Scheduled(fixedDelay = 300000)
public void deleteFileCache() {

    //remove files last accessed > 12h
    cache.entrySet().stream().filter(entry -> LocalDateTime.now().minus(12, ChronoUnit.HOURS)
            .isAfter(entry.getValue().getLastAccessed())).forEach(entry -> {
        File file = new File(tempFolder, entry.getKey());
        boolean deleted = file.delete();
    });

    //remove entries from HashMap
    cache.entrySet().removeIf(entry -> LocalDateTime.now().minus(12, ChronoUnit.HOURS)
            .isAfter(entry.getValue().getLastAccessed()));

    //if little space left remove oldest files
    long freeSpace = tempFolder.getFreeSpace();
    while (freeSpace < 6000000000L) {
        Optional<String> fileToDelete = cache.entrySet().stream()
                .min(Comparator.comparing(stringCachedFileEntry -> stringCachedFileEntry.getValue().getLastAccessed()))
                .map(Map.Entry::getKey);

        fileToDelete.ifPresent(filename -> {
            new File(tempFolder, filename).delete();
            cache.remove(filename);
        });
        freeSpace = tempFolder.getFreeSpace();
    }
}

此方法每天大约会失败 2-3 次,并出现 ConcurrentModificationException。我不明白为什么,因为该方法只能同时运行一次,并且我不会在从 HashMap 中删除的同时进行迭代。

它在行 .min(Comparator.comparing(stringCachedFileEntry ->...

java.util.ConcurrentModificationException: null
        at java.util.HashMap$EntrySpliterator.forEachRemaining(HashMap.java:1704) ~[na:1.8.0_212]
        at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) ~[na:1.8.0_212]
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) ~[na:1.8.0_212]
        at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) ~[na:1.8.0_212]
        at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) ~[na:1.8.0_212]
        at java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:479) ~[na:1.8.0_212]
        at java.util.stream.ReferencePipeline.min(ReferencePipeline.java:520) ~[na:1.8.0_212]
        at ch.my.app.server.FileCacheService.deleteFileCache(FileCacheService.java:113) ~[classes!/:1.0-SNAPSHOT]

最佳答案

ConcurrentModificationException 不仅是在迭代时从迭代集中进行删除时引发的。插入也会导致这种情况。因此,您的可能原因是您的 Spring boot Controller 在未与 deleteFileCache 方法同步的情况下写入映射。

显而易见的解决方案是使用一些线程安全映射而不是 HashMap,例如一些评论中提到的 ConcurrentHashMap

例如,Spliterator 的文档说:

… After binding a Spliterator should, on a best-effort basis, throw ConcurrentModificationException if structural interference is detected. …

从堆栈跟踪看来,min 方法使用了 Spliterator。

结构更改包括插入和删除(它们可能不包括替换映射中仅更改值的映射)。

链接: Documentation of Spliterator

关于java - 随机 java.util.ConcurrentModificationException : null,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58306123/

相关文章:

java - 我如何创建自定义 Jackson Mapper?

database - 如何在 Spring MVC Controller 中显示来自数据库的表

java - 更紧凑的单位矩阵 lambda 表达式

java - 如何使用java8流结合grouping by语句计算值的总和

java - 如何在 Xamarin 中获取 AccessibilityManager 实例

java - 动态设置 View 样式

java - java游戏使用TCP和UDP

java - JSP下拉不提交对象

java - 当 JSF/Spring 中的 session 超时时重定向到登录或当 session 停用(或给定时间空闲)时自动注销

java - 从 java 8 中没有 POJO 的对象列表或流中收集为 HashMap<String,String>