线程池,你会用吗?(没有做到精通的请进)

开发 前端
线程池是Java并发编程中的重要工具,无论是Java原生的Executor框架还是Guava库提供的扩展,都为我们提供了强大的异步任务处理能力。

在Java并发编程领域,线程池是一种至关重要的工具,它能显著提升应用程序的性能与资源管理效率。通过复用线程,线程池避免了频繁创建和销毁线程所带来的开销。在本教程中,我们将深入探讨Java和Guava库中线程池的使用。

一、Java中的线程池

(一)Executor框架

Java的java.util.concurrent包提供了Executor框架,这是管理线程池的核心。

Executor接口是该框架的基础,它定义了一个简单的方法execute(Runnable task),用于提交任务执行。

Executor接口本身并不直接管理线程,而是将任务的执行委托给实现类。

(二)ExecutorService

ExecutorService接口扩展了Executor接口,提供了更丰富的功能,用于管理线程池的生命周期以及任务的提交与执行。它包含了启动、关闭线程池的方法,以及提交任务并获取执行结果的方法。

2.1 创建线程池

在Java中,我们可以使用Executors类的静态方法来创建不同类型的线程池:

  • FixedThreadPool:创建一个固定大小的线程池,线程池中的线程数量在创建时就被确定,并且不会改变。如果提交的任务数量超过了线程池的容量,任务将被放入队列中等待执行。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
  • CachedThreadPool:创建一个可缓存的线程池,如果线程池中的线程在一段时间内没有被使用,它们将被回收。如果提交的任务数量超过了当前线程池中的线程数量,新的线程将被创建。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  • SingleThreadExecutor:创建一个单线程的线程池,它只使用一个线程来执行任务。所有提交的任务将按照顺序依次执行。
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  • ScheduledThreadPool:创建一个支持定时及周期性任务执行的线程池。可以安排任务在指定的延迟后执行,或者定期重复执行。
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);

2.2 提交任务

一旦创建了线程池,我们可以使用submit方法提交任务。submit方法有多种重载形式,可接受Runnable或Callable任务,并返回Future对象,通过Future对象可以获取任务的执行结果。

Future<Integer> future = fixedThreadPool.submit(() -> {
    // 执行任务并返回结果
    return 42;
});
try {
    Integer result = future.get();
    System.out.println("任务执行结果: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

2.3 关闭线程池

在应用程序结束时,我们需要正确关闭线程池,以确保所有任务都能正常完成,并释放资源。ExecutorService提供了shutdown和shutdownNow方法来实现这一点。

  • shutdown:启动一个有序关闭过程,不再接受新任务,但会继续执行已提交的任务。
fixedThreadPool.shutdown();
  • shutdownNow:尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务列表。
List<Runnable> tasks = fixedThreadPool.shutdownNow();

(三)示例:使用线程池进行并行计算

假设我们有一个简单的任务,需要计算一组数字的平方。我们可以使用线程池来并行执行这些计算,以提高效率。

public class ThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        List<Future<Integer>> futures = new ArrayList<>();

        List<Integer> numbers = List.of(1, 2, 3, 4, 5);
        for (int number : numbers) {
            Future<Integer> future = executorService.submit(() -> number * number);
            futures.add(future);
        }

        executorService.shutdown();

        for (Future<Integer> future : futures) {
            try {
                System.out.println("平方结果: " + future.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果是:

平方结果: 1
平方结果: 4
平方结果: 9
平方结果: 16
平方结果: 25

二、Guava中的线程池

Guava库提供了ListeningExecutorService接口,它扩展了ExecutorService,并提供了更方便的异步任务处理方式。ListeningExecutorService允许我们注册监听器,以便在任务完成时得到通知。

(一)创建ListeningExecutorService

在Guava中,我们可以使用MoreExecutors类的静态方法来创建ListeningExecutorService。例如,我们可以将一个普通的ExecutorService包装成ListeningExecutorService:

ExecutorService executorService = Executors.newFixedThreadPool(5);
ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);

(二)提交任务并注册监听器

提交任务后,我们可以使用Futures.addCallback方法注册一个回调,当任务完成时,回调的onSuccess或onFailure方法将被调用。

Future<Integer> future = listeningExecutorService.submit(() -> {
    // 执行任务并返回结果
    return 42;
});

Futures.addCallback(future, new FutureCallback<Integer>() {
    @Override
    public void onSuccess(Integer result) {
        System.out.println("任务成功执行,结果: " + result);
    }

    @Override
    public void onFailure(Throwable t) {
        System.out.println("任务执行失败: " + t.getMessage());
    }
});

(三)示例:使用Guava线程池进行异步任务处理

以下是一个完整的示例,展示如何使用Guava的线程池进行异步任务处理,并注册监听器来处理任务结果。

public class GuavaThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executorService);

        ListenableFuture<Integer> future = listeningExecutorService.submit(() -> {
            // 模拟任务执行
            Thread.sleep(2000);
            return 42;
        });

        final ExecutorService callbackExecutor = Executors.newFixedThreadPool(3);
        Futures.addCallback(future, new FutureCallback<Integer>() {
            @Override
            public void onSuccess(Integer result) {
                System.out.println("任务成功执行,结果: " + result);
                callbackExecutor.shutdown();
            }

            @Override
            public void onFailure(Throwable t) {
                System.out.println("任务执行失败: " + t.getMessage());
                callbackExecutor.shutdown();
            }
        }, callbackExecutor);

        // 关闭线程池
        executorService.shutdown();
    }
}

运行结果是:

任务成功执行,结果: 42

三、补充

补充一下Executors的工厂方法:

方法

描述

适用场景

newCachedThreadPool

创建一个可缓存的线程池。如果线程池的当前线程数超过了处理需求,则会回收空闲线程;如果需求增加,则可以添加新线程。

执行大量短期异步任务

newFixedThreadPool

创建一个固定大小的线程池。线程池中的线程数量固定,如果所有线程都在忙,新的任务会在队列中等待。

负载较重且任务量稳定的场景

newScheduledThreadPool

创建一个支持定时及周期性任务执行的线程池。可以调度命令在给定的延迟后运行,或定期执行。

需要定时执行任务的场景

newSingleThreadExecutor

创建一个单线程化的线程池。确保所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

需要保证任务顺序执行的场景

newSingleThreadScheduledExecutor

创建一个单线程的定时任务执行器。支持定时及周期性任务执行。

需要单线程执行定时任务的场景

newThreadPerTaskExecutor

创建一个为每个任务创建新线程的执行器。每个任务都会启动一个新的线程来执行。

任务之间完全独立且不需要复用线程的场景

newVirtualThreadPerTaskExecutor

创建一个为每个任务创建虚拟线程的执行器。虚拟线程是轻量级线程,适用于高并发场景。

需要高并发且任务量大的场景

newWorkStealingPool

创建一个工作窃取线程池。使用 ForkJoinPool 实现,线程池中的线程会主动“窃取”其他线程的任务来执行,提高 CPU 利用率。

计算密集型任务,可以充分利用多核处理器的优势

文末总结

线程池是Java并发编程中的重要工具,无论是Java原生的Executor框架还是Guava库提供的扩展,都为我们提供了强大的异步任务处理能力。通过合理使用线程池,我们可以有效提高应用程序的性能和资源利用率。在实际应用中,根据具体需求选择合适的线程池类型和使用方式至关重要。

责任编辑:武晓燕 来源: 看山的小屋
相关推荐

2021-11-03 17:40:51

Python线程

2021-09-16 11:02:49

Python线程

2023-06-08 07:48:03

Java线程池

2022-06-24 06:43:57

线程池线程复用

2024-03-06 08:15:03

@Autowired注入方式Spring

2021-05-21 12:36:16

限流代码Java

2020-06-04 14:15:55

Java中BigDecimal函数

2018-09-29 15:34:34

JavaList接口

2021-08-11 10:00:51

缓存MyBatis管理

2021-09-06 10:42:18

Linux命令服务器

2021-06-03 14:23:57

线程线程池JAVA

2021-06-01 10:49:22

线程池Java开发

2021-09-02 09:53:42

开发Redis配置

2020-11-09 09:03:35

高并发多线程ThreadLocal

2023-12-28 07:49:11

线程池源码应用场景

2019-07-25 12:46:32

Java高并发编程语言

2019-01-28 17:42:33

Python数据预处理数据标准化

2023-01-07 17:41:36

线程池并发

2019-10-31 08:36:59

线程内存操作系统

2023-12-01 11:13:50

JavaTreeSet
点赞
收藏

51CTO技术栈公众号