简体   繁体   中英

Java concurrency, stream behavior

Methods getFirst() and getSecond() of this class is invoked concurrently. It is a part of a web app.

Internal maps populated as well with no concurrency.

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;
    }
}

I tested it with something like ab -n 10000 -c 100 (Apache's utility). And I log it. I get the same set all times. But if I change map.get(key).stream() to map.get(key).parallelStream() and do the same steps I get sometimes different result size (always smaller).

What is it?

You are using TreeSet.addAll() inside the forEach of parallel stream. The forEach body can be executed simultaneously several times in different threads for different elements and TreeSet is not thread-safe. To fix the problem quickly you can either synchronize modification of result or use forEachOrdered . However it would be cleaner and more performant to flatMap your stream and collect it at once without forEach . Try this version:

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));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM