繁体   English   中英

JAVA:将列表拆分为较小的列表,然后在多个线程中流式传输它们

[英]JAVA: Split list into smaller lists and then stream them in multiple threads

我有一个数据库,其中有一个带有链接的表。

我设法发现我可以在分区的帮助下将列表拆分为更小的列表。 根据这篇文章似乎 Partition 类是最快的( https://e.printstacktrace.blog/divide-a-list-to-lists-of-n-size-in-Java-8/

在我将它们分成更小的列表后,我想使用这些链接并同时从它们中获取数据。 我可以使用一个列表,然后:

linkList.parallelStream().forEach(link -> {
        ScrapeLink(link);});

并设置 System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "5");

但是在我的情况下,我想将它们拆分为较小的列表,然后将 parallelStream 拆分为另一种方法,如果我使用 ScraperAPI 在一个会话中进行每个链接拆分(例如,使用 session_number 通过设置 session_number=123 重用相同的代理。)

所以当我有一个这样的列表时: final List links = Arrays.asList("link1","link2","link3","link4","link5","link6","link7");

System.out.println(Partition.ofSize(numbers, 3));

我将有 [[link1, link2, link3], [link4, link5, link6], [link7]] 但是当我想同时在多个线程中处理这些小链接列表时该怎么办?

我的想法是使用 Java 8 Streams。 但他们可能是更好的方法?

您可以使用默认 forkjoinpool(如您所提到的容量为 5)

以及为您的子列表定义的自定义线程池。

所以你需要先创建一个像这样的可运行类,稍后你将提交到你的“新”线程池

    @AllArgsConstructor
    public void LinkProcessorTask implements Runnable {
        private String link;
        
        @Override
        public void run() {
            //do something with your link in the sublist
        }
    }
    

    public void doWork() {

      List<List<String>> subListsOfLinks = .... // partitioning function

      subListsOfLinks.parallelStream().forEach(list -> {
          ExecutorService executorService = Executors.newFixedThreadPool(4 //concurrency);
          for(String link: list) {
              LinkProcessorTask linkProcessorTask = new LinkProcessorTask(link);
              executorService.submit(linkProcessorTask);
              executorService.awaitTermination(//Timeout);

          }
      })
    }

现在是您自己的设计决定是否要使这个新的线程池成为具有固定并发性的全局线程池。 或者你想在你的 ForkJoinPool 中调用。

如果你进入, total number of threads spawned = ForkJoinPoolConcurrency * CustomThreadPoolConcurrency.

否则它将只是ForkJoinPoolConcurrency + CustomThreadPoolConcurrency.

取决于您的机器等,多种因素。

您可以通过使用CountDownLatch来避免大量的 awaitTermination 方法,如果您想先等待一组的所有链接完成,然后继续进行。

不要使用流来安排工作,很容易将输入流(通过迭代器)连接到您的工作人员。 这不是必需的(除非任务非常快)但是,如果由于某种原因需要分块获取数据,则可以直接进行。

原因是您没有处理流数据,您将更好地控制任务的执行方式。

例如:

@SneakyThrows
public static void main(String... args) {

    int WORKERS = 5;
    int CHUNK = 5;

    Iterator<Integer> jobs = range(0, 47).iterator();

    List<Thread> workers = range(0, WORKERS).mapToObj(j -> new Thread(() -> {
        while(true) {
            int [] chunk = new int [CHUNK];
            int size = 0;
            synchronized (jobs) {
                while(size < CHUNK && jobs.hasNext())
                    chunk[size++] = jobs.next();
            }
            if(size == 0)
                break;
            slowJobProccesor(j, chunk);
        }
    })).collect(toList());

    for (Thread worker : workers)
        worker.start();

    for (Thread worker : workers)
        worker.join();

}

@SneakyThrows
private static void slowJobProccesor(int j, int[] n) {
    Thread.sleep(ThreadLocalRandom.current().nextInt(1_000, 1_500));
    System.out.printf("Thread #%d done job: %s%n", j, Arrays.stream(n).mapToObj(Integer::toString).collect(joining(", ")));
}

带输出

Thread #1 done job: 5, 6, 7, 8, 9
Thread #2 done job: 10, 11, 12, 13, 14
Thread #4 done job: 20, 21, 22, 23, 24
Thread #3 done job: 15, 16, 17, 18, 19
Thread #0 done job: 0, 1, 2, 3, 4
Thread #2 done job: 30, 31, 32, 33, 34
Thread #1 done job: 25, 26, 27, 28, 29
Thread #4 done job: 35, 36, 37, 38, 39
Thread #3 done job: 40, 41, 42, 43, 44
Thread #0 done job: 45, 46, 0, 0, 0

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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