java - 自定义谓词有时会返回随机结果

标签 java multithreading java-8 thread-safety predicate

我的要求是找出所有符合给定条件的匹配对象。 因此,我所做的就是创建一个自定义谓词并传递匹配条件以及应与对象匹配的源 ID。如果条件匹配,那么我将在内部填充一个 map 来保存详细信息。

当流完成时,该 map 将包含所有匹配条件的详细信息。 所以我用这张图来进行后面的计算。 大多数时候它可以工作,但开始随机失败。

我的自定义谓词是 -

public class IdentifierMapPredicate<T extends Identifiable, V> implements Predicate<T> {
    private static Logger           logger               = Logger
            .getLogger();
    private Collection<V>                  sourceIds;
    private BiPredicate<T, V>              matchingCondition;
    private Map<V, Collection<Identifier>> identifierMap;

    public Map<V, Collection<Identifier>> getIdentifierMap() {
        return identifierMap;
    }

    public IdentifierMapPredicate(Collection<V> sourceIds,
            BiPredicate<T, V> matchingCondition) {
        this.sourceIds = sourceIds;
        this.matchingCondition = matchingCondition;
        identifierMap = new HashMap<>(sourceIds.size());
    }

    @Override
    public boolean test(T t) {
        for (V id : sourceIds) {
            if (matchingCondition.test(t, id)) {
                //logger.debug("t :{}, id :{}", t.getIdentifier(), id);
                Collection<Identifier> ids = identifierMap.get(id);
                if(ids == null){
                    ids = new HashSet<>();
                    identifierMap.put(id, ids);
                }
                ids.add(t.getIdentifier());
                logger.debug("identifierMap :{}", identifierMap);
                return true;
            }
        }
        return false;
    }
}

当条件匹配并且我们更新 map 时,我添加了日志语句,有时 map 只有 1 个元素(即使我期望 2 个),有时甚至是 0。

2019-11-24 09:31:52.574 PST 调试 ForkJoinPool.commonPool-worker-5 IdentifierMapPredicate:52 - 标识符映射:{}

2019-11-24 09:31:52.574 PST 调试 ForkJoinPool.commonPool-worker-7 IdentifierMapPredicate:52 -identifierMap :{}

上面的实现有什么问题吗? 将其更改为 ConcurrentHashMap 会有帮助吗?

最佳答案

Most of the time it works, but starts failing randomly

是的,经典race condition .

您正在多线程上下文中使用IdentifierMapPredicate。如果您同时访问 IdentifierMapPredicate.test,线程也会同时使用 identifierMap,这不是线程安全的。

看起来 ConcurrentHashMap 可以解决这个问题。或者,您可以使用 synchronized 关键字修改方法 test ,但这会给您带来更少的吞吐量/更多的锁定(我认为)。但另一方面,也减少了头痛,这是一种权衡,如果没有严格的性能要求,我很乐意接受减少头痛。只是我个人的喜好。

顺便说一句。现在你可以使用 map 。 computeIfAbsent创建新条目而不是

Collection<Identifier> ids = identifierMap.get(id);
if(ids == null){
   ids = new HashSet<>();
   identifierMap.put(id, ids);
}

关于java - 自定义谓词有时会返回随机结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59020638/

相关文章:

在远程机器linux上找不到java命令

java - 使用 Java 从现有 Excel 文件读取单元格并将其写入新的 Excel 文件

java - Struts 没有调用我的 servlet。我究竟做错了什么?

java - 访问外部类中的静态变量时静态内部线程的问题

ios - 混淆线程和队列之间的技术术语

java - ScheduledExecutorService 工作线程在 FutureTask.cancel(true) 之后保持中断状态

java - Stream<Set<Path>> 到 Set<Path>

java - Mockito - 创建一个模拟为 Spring Bean

java - opencsv 写入文件,其中包含一些引用的元素和其他未引用的元素

java - RxJava1 和 RxJava2 的区别