如何让 main 等待线程执行,但这些线程必须同时启动

How to make main wait for threads to execute, but these threads must be started at the same time

提问人:zotov_l88 提问时间:11/13/2023 最后编辑:zotov_l88 更新时间:11/13/2023 访问量:39

问:

我有一个程序,在其中创建一个数组,克隆到所需数量的相同数组中,然后以不同的方式排序。

https://github.com/zotov88/algorithms

public class SortingRunner {

    final static int CAPACITY = 10_000;
    final static int FROM = -100;
    final static int TO = 100;
    final static int COUNT_ARRAYS = 4;

    public static void main(String[] args) throws InterruptedException {

        int[][] arrays = Utils.generateTheSameRandomArrays(CAPACITY, FROM, TO, COUNT_ARRAYS);

         List<Sorting> sortingList = List.of(
                new BubbleSort(arrays[0]),
                new SelectionSort(arrays[1]),
                new MergeSort(arrays[2]),
                new QuickSort(arrays[3])
        );

        for (Sorting sorting : sortingList) {
            Thread t = new Thread(() -> Utils.speedTest(sorting));
            t.start();
            t.join();
        }

        Utils.printArrays(arrays);
    }
}

在这段代码中,首先对线程进行逐个处理,然后在主线程中开始数组的打印。


        for (Sorting sorting : sortingList) {
            Thread t = new Thread(() -> Utils.speedTest(sorting));
            t.start();
            t.join();
        }

        Utils.printArrays(arrays);

我希望数组在每个线程中同时排序,并且 main 等待它们完成,然后打印。

Java 多线程并

评论

1赞 tkausl 11/13/2023
不要立即加入它们,在所有线程启动后加入它们。
2赞 MadProgrammer 11/13/2023
考虑使用 - 请参阅执行程序跟踪Executor
0赞 zotov_l88 11/13/2023
@tkausl,谢谢。就是这么简单
0赞 Sagar 11/14/2023
运行的机器应该有 5 个内核才能实际并行运行,因为您正在测量时间,并且每个线程的启动都可以使用倒计时闩锁触发

答:

0赞 Basil Bourque 11/13/2023 #1

tl;博士

使用执行程序服务。

try (
        ExecutorService executorService = Executors. … ;
)
{
    futures = executorService.invokeAll ( tasks );
}

执行器框架

Java 5 中添加了 Executors 框架来支持此类工作。从那时起,我们很少需要直接互动。Thread

将任务定义为 RunnableCallable 对象。将它们提交到具有所需行为的执行程序服务对象。

通常最好使用 Executors 实用程序类来实例化执行器服务。在这里,我们使用一个执行器服务,为每个任务创建一个新的虚拟线程

提交任务时,将获得一个 Future 对象。使用该对象检查任务的进度。在 的情况下,使用该对象访问任务的结果。CallableFuture

您可以一次提交一个任务。或者,您可以使用来提交任务集合。没有真正的区别,只是为了方便您的编码情况。invokeAll

如何让 main 等待线程执行

您可以等待执行程序服务完成其所有任务,在此之前原始线程将被阻止。要管理此等待完成的块,您有两条路由,一条是手动路由,另一条是更方便的自动路由:

  • 要手动管理,请参阅 Javadoc for ExecutorService 接口上提供给您的样板代码。
  • 从 Java 19 开始,对象是 AutoCloseable。因此,我们可以使用 try-with-resources 语法来更方便地等待完成。(您可能需要检查 OpenJDK 中的源代码,以查看该行为是否符合您的需求。ExecutorService

我们在下面的代码中使用更方便的 try-with-resources 方法。

但这些线程必须同时启动

这是不可能的。Java 中的线程由主机操作系统管理的本机线程提供支持。这些操作系统线程的调度超出了您的控制范围,也超出了 JVM 的控制范围。

执行程序服务将尝试立即开始执行任务(除非您使用 ScheduledExecutorService 指定了延迟)。因此,由多核机器上的多个线程支持的执行器服务可能会几乎在同一时刻开始执行多个任务。但也许不是。例如,如果计算机负担过重,则某些任务可能需要等待。或者,主机操作系统可能会优先处理其他线程,然后再处理您的线程。您通常可以预期您的任务很快就会开始,但不是确定性的。

示例代码

package work.basil.example.threading;

import java.time.Instant;
import java.util.List;
import java.util.concurrent.*;

public class BunchOfTasks
{
    public static void main ( String[] args )
    {
        // Define your tasks to be performed.
        List < Callable < Instant > > tasks =
                List.of (
                        ( ) -> {
                            System.out.println ( "x" );
                            return Instant.now ( );
                        } ,
                        ( ) -> {
                            System.out.println ( "y" );
                            return Instant.now ( );
                        } ,
                        ( ) -> {
                            System.out.println ( "z" );
                            return Instant.now ( );
                        }
                );

        // Submit your tasks. Capture `Future` objects. 
        List < Future < Instant > > futures = List.of ( );
        try (
                ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor ( ) ;
        )
        {
            futures = executorService.invokeAll ( tasks );
        } catch ( InterruptedException e ) { throw new RuntimeException ( e ); }

        // Report results by inspecting `Future` objects.
        for ( Future < Instant > instantFuture : futures )
        {
            try { System.out.println ( instantFuture.get ( ).toString ( ) ); } catch ( InterruptedException | ExecutionException e ) { throw new RuntimeException ( e ); }
        }
    }
}

输出示例:

x
y
z
2023-11-12T20:51:42.098634Z
2023-11-12T20:51:42.098742Z
2023-11-12T20:51:42.098795Z