ExecutorService 的理解和使用

本文详细介绍了Java中四种线程池的使用方法及其特点:newCachedThreadPool创建可缓存线程池;newFixedThreadPool创建定长线程池;newScheduledThreadPool创建支持定时任务的线程池;newSingleThreadExecutor创建单线程化线程池。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

我们之前使用线程的时候都是使用new Thread来进行线程的创建,但是这样会有一些问题。如:

a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

而我们今天来学习和掌握另外一个新的技能,特别像一个线程池的一个接口类ExecutorService,下面我们来了解下java中Executors的线程池

Java通过Executors提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

(1). newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下:

 

 

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
    final int index = i;
    try {
        Thread.sleep(index * 1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    cachedThreadPool.execute(new Runnable() {

        @Override
        public void run() {
            System.out.println(index);
        }
    });
}

 

 

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
 
(2). newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

 

 

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
    final int index = i;
    fixedThreadPool.execute(new Runnable() {


        @Override
        public void run() {
            try {
                System.out.println(index);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}

 

 

因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()。可参考PreloadDataCache。
 
(3) newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

 

 

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {

    @Override
    public void run() {
        System.out.println("delay 3 seconds");
    }
}, 3, TimeUnit.SECONDS);

 

 

表示延迟3秒执行。
 
定期执行示例代码如下:

 

 

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

@Override
public void run() {
System.out.println("delay 1 seconds, and excute every 3 seconds");
}
}, 1, 3, TimeUnit.SECONDS);

 

 

表示延迟1秒后每3秒执行一次。
ScheduledExecutorService比Timer更安全,功能更强大,后面会有一篇单独进行对比。
 
(4)、newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:

 

 

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
    final int index = i;
    singleThreadExecutor.execute(new Runnable() {

        @Override
        public void run() {
            try {
                System.out.println(index);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    });
}
博客来源:https://www.cnblogs.com/zhaoyan001/p/7049627.html
### 关于 CompletableFuture ExecutorService 的关系及用法 #### 1. 基本概念 `CompletableFuture` 是 Java 8 引入的一个类,它扩展了 `Future` 接口的功能,提供了更加丰富的异步编程支持[^1]。而 `ExecutorService` 则是一个接口,主要用于管理调度线程池中的任务。 `CompletableFuture` 默认会使用 `ForkJoinPool.commonPool()` 来执行异步操作,但如果需要指定特定的线程池,则可以通过 `ExecutorService` 提供的实例来进行配置[^2]。 --- #### 2. 联系 两者之间的联系在于 `CompletableFuture` 可以与 `ExecutorService` 结合使用。通过这种方式,开发者可以更好地控制线程资源分配以及任务的执行环境。例如,在创建 `CompletableFuture` 实例时,可以通过重载的方法传入一个自定义的 `Executor` 对象: ```java ExecutorService customThreadPool = Executors.newFixedThreadPool(4); CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { System.out.println("Running task in a separate thread"); }, customThreadPool); ``` 上述代码展示了如何利用 `runAsync` 方法结合自定义线程池完成异步任务的提交[^3]。 --- #### 3. 区别 尽管二者能够协同工作,但在功能定位上存在显著差异: - **灵活性**: `ExecutorService` 主要关注的是线程池管理及其生命周期控制;相比之下,`CompletableFuture` 更侧重于描述复杂的异步流程逻辑,比如链式调用、组合多个未来结果等[^4]。 - **易读性 vs 复杂度**: 当业务场景涉及多阶段依赖或者条件分支较多的情况下,基于 `CompletableFuture` 编写的程序可能会显得较为晦涩难以维护。此时采用传统的同步风格加上显式的锁机制反而可能让代码更容易理解。 - **异常处理**: 如果某个子任务抛出了未被捕获的运行时异常,默认情况下该异常会被记录下来并向上传播给最终消费者。值得注意的一点是,即使上游环节出现了问题也不会造成整个应用程序挂起等待超时等问题——因为一旦检测到状态变化便会立即通知下游组件采取相应措施[^5]。 --- #### 4. 示例代码 下面给出一段综合运用两者的例子,演示如何在一个固定大小的工作队列里按顺序依次启动若干独立计算单元并将它们的结果汇总起来返回给调用方: ```java import java.util.concurrent.*; import java.util.stream.Collectors; import java.util.List; public class Example { public static void main(String[] args) throws Exception { ExecutorService pool = Executors.newCachedThreadPool(); List<CompletableFuture<Integer>> futures = IntStream.rangeClosed(1, 5).boxed() .map(i -> CompletableFuture.supplyAsync(() -> doWork(i), pool)) .collect(Collectors.toList()); CompletableFuture<List<Integer>> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .thenApply(v -> futures.stream().map(CompletableFuture::join).toList()); System.out.println(allOf.get()); // 输出所有已完成的任务结果列表 pool.shutdown(); } private static Integer doWork(int taskId){ try { Thread.sleep((long)(Math.random()*1000)); } catch (InterruptedException e){} return taskId * 2; // 模拟耗时运算过程 } } ``` 此片段清晰地体现了借助外部提供的执行器服务对象定制化行为的能力的同时也保留了内部强大的非阻塞性质特性优势。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值