Java并发,流行为

时间:2015-05-07 10:22:12

标签: java concurrency java-8 java.util.concurrent

同时调用此类的方法getFirst()getSecond()。它是网络应用程序的一部分。

内部地图也填充,没有并发。

public class MyClass {
    private Map<String, List<List<String>>> first;

    private Map<String, List<List<String>>> second;

    public MyClass() {
        first = new ConcurrentHashMap<>();
        second = new ConcurrentHashMap<>();
    }

    public Set<String> getFirst(String key, String token, int a, int b) {
        return get(first, key, token, a, b);
    }

    public Set<String> getSecond(String key, String token, int a, int b) {
        return get(second, key, token, a, b);
    }

    private Set<String> get(final Map<String, List<List<String>>> map, final String key, final String token,
        final int a, final int b) {
        Set<String> result = new TreeSet<>();
        map.get(key).stream().filter(i -> i.size() <= b && i.size() >= a).forEach(
            s -> result
                .addAll(s.stream().filter(p -> StringUtils.containsIgnoreCase(p, token)).collect(Collectors.toList())));
        return result;
    }
}

我用ab -n 10000 -c 100(Apache的实用程序)测试了它。我记录下来了。我总是得到同样的一套。但是,如果我将map.get(key).stream()更改为map.get(key).parallelStream()并执行相同的步骤,我有时会得到不同的结果大小(总是更小)。

这是什么?

1 个答案:

答案 0 :(得分:5)

您在TreeSet.addAll()并行流中使用forEach。对于不同的元素,forEach主体可以在不同的线程中同时执行多次,TreeSet不是线程安全的。要快速解决问题,您可以同步result的修改或使用forEachOrdered。但是,对于flatMap您的信息流,它会更清晰,更高效,并且在没有forEach的情况下立即收集它。试试这个版本:

return map.get(key).stream()
        .filter(i -> i.size() <= b && i.size() >= a)
        .flatMap(List::stream).filter(p -> StringUtils.containsIgnoreCase(p, token))
        .collect(Collectors.toCollection(TreeSet::new));