Java线程池ExecutorService学习和使用

一、概述

Java中的 ExecutorService是一个用于管理和控制线程的接口,它是Java并发库的一部分。ExecutorService提供了比直接使用线程更强大的功能,使得线程管理更简单、灵活和高效。本文将详细介绍 ExecutorService的基本概念、常用方法、配置及其在实际应用中的使用。

二、基本概念

ExecutorService提供了一种将任务提交与任务执行分离的机制。通过使用 ExecutorService,可以创建一个线程池,将任务提交到线程池中执行,而不必手动管理线程的创建和销毁。

三、常用方法

ExecutorService接口提供了多种方法来管理线程和任务。以下是一些常用方法:

  1. 提交任务

    • submit(Runnable task): 提交一个Runnable任务,返回一个Future对象。
    • submit(Callable<T> task): 提交一个Callable任务,返回一个Future对象。
  2. 关闭线程池

    • shutdown(): 启动有序关闭,之前提交的任务会继续执行,但不会接受新任务。
    • shutdownNow(): 试图停止所有正在执行的任务,暂停等待任务,并返回等待执行任务的列表。
  3. 获取线程池状态

    • isShutdown(): 如果线程池已关闭,则返回true。
    • isTerminated(): 如果线程池已终止,则返回true。
  4. 批量提交任务

    • invokeAll(Collection<? extends Callable<T>> tasks): 执行给定的任务,当所有任务完成时,返回一个包含其状态和结果的Future列表。
    • invokeAny(Collection<? extends Callable<T>> tasks): 执行给定的任务,当一个任务成功完成(没有抛出异常)时,返回该任务的结果。

四、线程池的创建

Java并发库提供了一些工厂方法来创建不同类型的线程池:

  1. 固定大小线程池

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
  2. 单线程线程池

    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  3. 可缓存线程池

    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  4. 定时线程池

    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

五、使用示例

以下是一个使用 ExecutorService的示例,展示了如何提交任务和处理结果。

import java.util.concurrent.*;

public class ExecutorServiceExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);

        Runnable task1 = () -> {
            System.out.println("Executing Task1");
        };

        Callable<String> task2 = () -> {
            return "Task2 Completed";
        };

        executorService.submit(task1);
        Future<String> future = executorService.submit(task2);

        try {
            String result = future.get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        executorService.shutdown();
    }
}

六、线程池配置

配置线程池参数可以更好地控制其行为,以满足不同的应用需求。以下是一些常见配置:

  1. 核心线程数和最大线程数

    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        2,  // corePoolSize
        5,  // maximumPoolSize
        60, // keepAliveTime
        TimeUnit.SECONDS,
        new LinkedBlockingQueue<Runnable>()  // workQueue
    );
  2. 自定义拒绝策略

    ThreadPoolExecutor executor = new ThreadPoolExecutor(
        2, 
        5, 
        60, 
        TimeUnit.SECONDS, 
        new LinkedBlockingQueue<Runnable>(),
        new ThreadPoolExecutor.AbortPolicy()  // 默认策略,抛出RejectedExecutionException
    );
  3. 钩子函数
    可以重写 ThreadPoolExecutorbeforeExecuteafterExecute方法来添加钩子函数,以便在任务执行前后进行额外操作。

七、实战示例

示例1:使用ScheduledExecutorService进行定时任务调度
import java.util.concurrent.*;

public class ScheduledExecutorServiceExample {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);

        Runnable task = () -> System.out.println("Executing Task at " + System.nanoTime());

        scheduledExecutorService.scheduleAtFixedRate(task, 0, 2, TimeUnit.SECONDS);

        try {
            Thread.sleep(10000);  // 让调度执行一段时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        scheduledExecutorService.shutdown();
    }
}
示例2:使用线程池执行大量任务并等待其完成
import java.util.concurrent.*;

public class BulkTaskExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);

        Callable<String> task = () -> {
            TimeUnit.SECONDS.sleep(1);
            return Thread.currentThread().getName();
        };

        List<Future<String>> futures = new ArrayList<>();
        for (int i = 0; i < 20; i++) {
            futures.add(executorService.submit(task));
        }

        futures.forEach(future -> {
            try {
                System.out.println(future.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });

        executorService.shutdown();
    }
}

思维导图

+------------------------------------------------------+
|          Java线程池ExecutorService学习和使用          |
+------------------------------------------------------+
           |
           +-----------------------------+
           | 一、概述                    |
           +-----------------------------+
           |
           +-----------------------------+
           | 二、基本概念                 |
           +-----------------------------+
           |
           +-----------------------------+
           | 三、常用方法                 |
           | 1. 提交任务                 |
           | 2. 关闭线程池               |
           | 3. 获取线程池状态           |
           | 4. 批量提交任务             |
           +-----------------------------+
           |
           +-----------------------------+
           | 四、线程池的创建             |
           | 1. 固定大小线程池           |
           | 2. 单线程线程池             |
           | 3. 可缓存线程池             |
           | 4. 定时线程池               |
           +-----------------------------+
           |
           +-----------------------------+
           | 五、使用示例                 |
           +-----------------------------+
           |
           +-----------------------------+
           | 六、线程池配置               |
           | 1. 核心线程数和最大线程数   |
           | 2. 自定义拒绝策略           |
           | 3. 钩子函数                 |
           +-----------------------------+
           |
           +-----------------------------+
           | 七、实战示例                 |
           | 示例1:定时任务调度          |
           | 示例2:执行大量任务并等待完成|
           +-----------------------------+

总结

通过学习和使用Java中的 ExecutorService,可以显著提升并发编程的效率和代码的可维护性。合理配置线程池参数,结合实际应用场景,可以实现高效、可靠的并发处理。希望本文提供的示例和思路能够帮助开发者深入理解并应用 ExecutorService,实现更高效的并发程序。

蓝易云是一家专注于香港及国内数据中心服务的提供商,提供高质量的服务器租用和云计算服务、包括免备案香港服务器、香港CN2、美国服务器、海外高防服务器、国内高防服务器、香港VPS等。致力于为用户提供稳定,快速的网络连接和优质的客户体验。
最后修改:2024 年 07 月 22 日
如果觉得我的文章对你有用,请随意赞赏