首页 > 解决方案 > 如何最有效地将一些信息添加到 Java 中的多个线程列表中?

问题描述

我有一些任务以最有效的方式聚合来自多个链接的一些信息,使用多线程。链接位于某个数组中。到目前为止,我有这样的解决方案:

Arrays.stream(link).parallel().forEach(link -> {
        try {
            String result = doSomeJobWithLink(link);
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    });

它工作得很好(工作已经完成了 2 秒)。

但我不想在我的 try 块中打印结果,而是在某个列表(或其他集合)中收集结果,所以我这样做了:

List<String> resultList = Collections.synchronizedList(new ArrayList<>());
Arrays.stream(link).parallel().forEach(link -> {
        try {
            String result = doSomeJobWithLink(link);
            resultList.add(result);
        } catch (IOException e) {
            e.printStackTrace();
        }
    });
resultList.forEach(System.out::println);

但它花了大约 5-8 秒而不是两秒。我可以以某种方式加快速度吗?

标签: javamultithreadinglistconcurrency

解决方案


当您这样做时Collections.synchronizedList(new ArrayList<>()),您将 asynchronized放在整个列表上,即列表上的任何操作即使读取也共享相同的互斥锁,这会降低性能并且是限制因素。

更好的方法是只收集到普通列表,收集器保证无序并发减少。

对于并发收集器,实现可以自由(但不要求)同时实现归约。并发减少是从多个线程同时调用累加器函数的一种方法,使用相同的并发可修改结果容器,而不是在累加期间保持结果隔离。仅当收集器具有 Collector.Characteristics.UNORDERED 特征或原始数据无序时,才应应用并发减少。

所以下面应该显着提高性能,

List<String> resultList = Arrays.stream(link).parallel().map(e -> {
            try {
                return doSomeJobWithLink(e);
            } catch (IOException ex) {
                ex.printStackTrace();
                return null;
            }
            return result;
        }).filter(Objects::nonNull).collect(Collectors.toList());

尽管不建议吞下异常,除非这是不可避免的。


推荐阅读