Random java.util.ConcurrentModificationException:null

问题描述 投票:0回答:1

我有一种方法,每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]
java spring java-stream concurrentmodification
1个回答
1
投票

A ConcurrentModificationException不仅被迭代时从迭代集中删除而抛出。插入也会导致它。因此,可能的原因是您的Spring引导控制器写入映射时未与deleteFileCache方法同步。

显而易见的解决方案是使用某些线程安全映射代替HashMap,例如,在一些注释中提到的ConcurrentHashMap

例如,Spliterator的文档说:

…绑定分隔符后,应尽力而为ConcurrentModificationException如果存在结构性干扰检测到。 …

从您的堆栈跟踪看来,min方法使用了分隔符。

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

© www.soinside.com 2019 - 2024. All rights reserved.