Java ExecutorService 和线程池教程

x33g5p2x  于2021-10-16 转载在 Java  
字(4.0k)|赞(0)|评价(0)|浏览(289)

欢迎阅读我的 Java 并发教程系列的第三部分。在本教程中,我们将学习如何使用执行器和线程池管理应用程序中的线程。

Executors 框架

在之前的教程中,我们学习了如何通过扩展 Thread 类或实现 Runnable 接口在 Java 中创建线程。

虽然创建一两个线程并运行它们很容易,但当您的应用程序需要创建 20 或 30 个线程来并发运行任务时,就会出现问题。

此外,可以毫不夸张地说,大型多线程应用程序将同时运行数百甚至数千个线程。因此,将线程创建和管理与应用程序的其余部分分开是有意义的。

进入 Executors,一个用于创建和管理线程的框架。 Executors 框架帮助你 -

线程创建:它提供了各种创建线程的方法,更具体地说是一个线程池,您的应用程序可以使用它来并发运行任务。
1.
线程管理:管理线程池中线程的生命周期。在提交任务执行之前,您无需担心线程池中的线程是否处于活动状态或忙碌状态或死状态。
1.
任务提交和执行:Executors 框架提供了在线程池中提交任务执行的方法,也赋予你决定任务何时执行的权力。例如,您可以提交一个现在执行的任务,或者安排它们稍后执行,或者让它们定期执行。

Java Concurrency API 定义了以下三个执行器接口,涵盖了创建和管理线程所需的一切 -

Executor - 一个简单的接口,包含一个名为 execute() 的方法来启动由 Runnable 对象指定的任务。
*
ExecutorService - Executor 的子接口,添加了管理任务生命周期的功能。它还提供了一个 submit() 方法,它的重载版本可以接受一个 Runnable 以及一个 Callable 对象。 Callable 对象与 Runnable 类似,不同之处在于 Callable 对象指定的任务也可以返回一个值。我们将在下一篇博文中更详细地了解 Callable。
*
ScheduledExecutorService - ExecutorService 的子接口。它添加了安排任务执行的功能。

除了上述三个接口外,API 还提供了一个 Executors 类,其中包含用于创建不同类型的执行器服务的工厂方法。

ExecutorService 示例

好的!现在让我们深入研究一个例子,以更好地理解事情。在下面的例子中,我们首先创建一个带有单个工作线程的 ExecutorService,然后在工作线程内部提交一个要执行的任务。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorsExample {
    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Executor Service...");
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        System.out.println("Creating a Runnable...");
        Runnable runnable = () -> {
            System.out.println("Inside : " + Thread.currentThread().getName());
        };

        System.out.println("Submit the task specified by the runnable to the executor service.");
        executorService.submit(runnable);
    }
}
# Output
Inside : main
Creating Executor Service...
Creating a Runnable...
Submit the task specified by the runnable to the executor service.
Inside : pool-1-thread-1

上面的例子展示了如何创建一个执行器服务并在执行器内部执行任务。我们使用 Executors.newSingleThreadExecutor() 方法创建一个 ExecutorService,它使用单个工作线程来执行任务。如果一个任务被提交执行,而该线程当前正忙于执行另一个任务,那么新任务将在队列中等待,直到线程空闲来执行它。

如果你运行上面的程序,你会注意到这个程序永远不会退出,因为执行器服务一直在监听新任务,直到我们明确地关闭它。

关闭 ExecutorService

ExecutorService 提供了两种关闭执行器的方法 -

shutdown() - 当 shutdown() 方法在执行器服务上被调用时,它停止接受新任务,等待之前提交的任务执行,然后终止执行器。
*
shutdownNow() - 此方法中断正在运行的任务并立即关闭执行程序。

让我们在程序末尾添加关闭代码,以便它优雅地退出 -

System.out.println("Shutting down the executor");
executorService.shutdown();

ExecutorService 多线程和任务示例

在前面的示例中,我们创建了一个使用单个工作线程的 ExecutorService。但是当我们创建一个线程池并在线程池中并发执行多个任务时,ExecutorService 的真正威力就来了。

以下示例显示了如何创建使用线程池并同时执行多个任务的执行程序服务 -

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecutorsExample {
    public static void main(String[] args) {
        System.out.println("Inside : " + Thread.currentThread().getName());

        System.out.println("Creating Executor Service with a thread pool of Size 2");
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Runnable task1 = () -> {
            System.out.println("Executing Task1 inside : " + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException ex) {
                throw new IllegalStateException(ex);
            }
        };

        Runnable task2 = () -> {
            System.out.println("Executing Task2 inside : " + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (InterruptedException ex) {
                throw new IllegalStateException(ex);
            }
        };

        Runnable task3 = () -> {
            System.out.println("Executing Task3 inside : " + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException ex) {
                throw new IllegalStateException(ex);
            }
        };


        System.out.println("Submitting the tasks for execution...");
        executorService.submit(task1);
        executorService.submit(task2);
        executorService.submit(task3);

        executorService.shutdown();
    }
}
# Output
Inside : main
Creating Executor Service with a thread pool of Size 2
Submitting the tasks for execution...
Executing Task2 inside : pool-1-thread-2
Executing Task1 inside : pool-1-thread-1
Executing Task3 inside : pool-1-thread-1

在上面的例子中,我们创建了一个 executor 服务,它有一个大小为 2 的固定线程池。固定线程池是一种非常常见的线程池,在多线程应用程序中经常使用。

在固定线程池中,执行程序服务确保池中始终运行指定数量的线程。如果任何线程由于某种原因死亡,它会立即被新线程替换。

当提交新任务时,执行程序服务从池中选择一个可用线程并在该线程上执行任务。如果我们提交的任务多于可用线程数,并且所有线程当前都在忙于执行

相关文章

微信公众号

最新文章

更多