24.jdk源码阅读之ExecutorService

1. 写在前面

ExecutorService 是一个非常重要的接口,用于管理和调度线程池。在日常工作中我们应该也是用的比较多,但是你真正了解它的底层实现原理吗?下面的几个问题在日常开发中你是否思考过?面试中是否被问到过?

  1. 什么是 ExecutorService?它与 Executor 有什么区别?
  2. 如何优雅地关闭 ExecutorService?
  3. 如何提交一个带返回值的任务?
  4. 如何处理任务执行中的异常?
  5. 什么是 invokeAll 和 invokeAny 方法?
  6. 如何创建一个自定义线程池?
  7. 什么是 ScheduledExecutorService?如何使用它调度任务?
  8. 如何在 ExecutorService 中处理大量短时间任务?

2. 全局视角

在这里插入图片描述

2.1 Executor 接口

ExecutorService 继承自 Executor 接口,该接口是 Java 并发包中最基础的接口之一。它定义了一个方法:

public interface Executor {
    void execute(Runnable command);
}

2.2 ExecutorService 接口

ExecutorService 扩展了 Executor 接口,增加了更多用于管理任务和线程池生命周期的方法:

public interface ExecutorService extends Executor {
    void shutdown();
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
    <T> Future<T> submit(Callable<T> task);
    <T> Future<T> submit(Runnable task, T result);
    Future<?> submit(Runnable task);
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
    <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

这些方法后面我们会展开介绍。

2.3 具体实现类

ExecutorService 有多个具体的实现类,分别适用于不同的场景:

2.3.1 ThreadPoolExecutor

  • 最常用的实现类,可以通过多种方式配置线程池的行为。
  • 直接或间接通过 Executors 工具类创建,如 Executors.newFixedThreadPool(),Executors.newCachedThreadPool() 等。

2.3.2 ScheduledThreadPoolExecutor

  • 扩展自 ThreadPoolExecutor,实现了 ScheduledExecutorService 接口,支持任务调度。
  • 可以通过 Executors.newScheduledThreadPool() 创建。

2.3.3 ForkJoinPool

  • 实现了 ExecutorService 和 ForkJoinPool 接口,适用于分治任务。
  • 主要用于并行计算,支持工作窃取算法。

3. submit()方法的底层实现

ExecutorService 接口的 submit 方法用于提交任务并返回一个 Future 对象,通过这个 Future 对象可以获取任务的执行结果或取消任务。Java 8中的 ExecutorService 接口有多个实现类,其中最常用的是 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor。
在这里插入图片描述
submit接口 不同的实现类有不同的实现,如下图所示:
在这里插入图片描述

3.1 ThreadPoolExecutor 的 submit 方法实现

ThreadPoolExecutor 是 ExecutorService 的核心实现类,提供了灵活的线程池配置。它继承自 AbstractExecutorService,并使用该抽象类提供的 submit 方法的默认实现。
AbstractExecutorService 提供了 submit 方法的默认实现:

public abstract class AbstractExecutorService implements ExecutorService {

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task, result);
        execute(ftask);
        return ftask;
    }

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }

    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
}
  1. 参数检查
    检查传入的任务是否为 null,如果是则抛出 NullPointerException。
  2. 创建 RunnableFuture
    使用 newTaskFor 方法将 Runnable 或 Callable 任务包
  3. 执行任务
    调用 execute 方法将 RunnableFuture 提交给线程池执行。
  4. 返回 Future
    返回 RunnableFuture 对象,该对象同时实现了 Future 接口,可以用于获取任务的结果或取消任务。

3.2 ScheduledThreadPoolExecutor 的 submit 方法实现

ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor,并实现了 ScheduledExecutorService 接口。它提供了调度任务的功能,但对于 submit 方法,它直接继承了 ThreadPoolExecutor 的实现。
ScheduledThreadPoolExecutor 类

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {

    // 省略了调度任务相关的方法

    // submit 方法直接继承自 ThreadPoolExecutor
}

FutureTask 类
FutureTask 是 RunnableFuture 的一个实现类,主要用于包装 Runnable 和 Callable 任务。它的构造函数如下:

public class FutureTask<V> implements RunnableFuture<V> {
    public FutureTask(Callable<V> callable) {
        // 初始化
    }

    public FutureTask(Runnable runnable, V result) {
        // 初始化
    }

    // 实现了 Runnable 和 Future 的方法
}

4. invokeAll()的底层实现

在这里插入图片描述
ExecutorService 接口的 invokeAll 方法用于批量提交一组 Callable 任务,并返回一个 Future 列表,通过这些 Future 对象可以获取每个任务的执行结果。

4.1 ExecutorService 接口中的 invokeAll 方法

ExecutorService 接口定义了两个 invokeAll 方法:

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

4.2 AbstractExecutorService 中的 invokeAll 方法

AbstractExecutorService 提供了 invokeAll 方法的默认实现:

public abstract class AbstractExecutorService implements ExecutorService {

    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
        if (tasks == null) throw new NullPointerException();
        ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
        boolean done = false;
        try {
            for (Callable<T> task : tasks) {
                RunnableFuture<T> f = newTaskFor(task);
                futures.add(f);
                execute(f);
            }
            for (Future<T> f : futures) {
                if (!f.isDone()) {
                    try {
                        f.get();
                    } catch (CancellationException ignore) {
                    } catch (ExecutionException ignore) {
                    }
                }
            }
            done = true;
            return futures;
        } finally {
            if (!done) {
                for (Future<T> f : futures) {
                    f.cancel(true);
                }
            }
        }
    }

    public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
        if (tasks == null || unit == null) throw new NullPointerException();
        long nanos = unit.toNanos(timeout);
        ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
        boolean done = false;
        try {
            for (Callable<T> task : tasks) {
                futures.add(newTaskFor(task));
            }

            final long deadline = System.nanoTime() + nanos;
            for (Future<T> f : futures) {
                execute((Runnable) f);
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    return futures;
                }
            }

            for (Future<T> f : futures) {
                if (!f.isDone()) {
                    if (nanos <= 0L) {
                        return futures;
                    }
                    try {
                        f.get(nanos, TimeUnit.NANOSECONDS);
                    } catch (CancellationException ignore) {
                    } catch (ExecutionException ignore) {
                    } catch (TimeoutException toe) {
                        return futures;
                    }
                    nanos = deadline - System.nanoTime();
                }
            }
            done = true;
            return futures;
        } finally {
            if (!done) {
                for (Future<T> f : futures) {
                    f.cancel(true);
                }
            }
        }
    }

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }
}

invokeAll(Collection<? extends Callable> tasks) 主要步骤介绍:

4.2.1 参数检查

检查传入的任务集合是否为 null,如果是则抛出 NullPointerException。

4.2.2 任务初始化

  1. 创建一个 ArrayList 来存储 Future 对象。
  2. 遍历任务集合,将每个 Callable 任务包装成 RunnableFuture 对象,并将其添加到 futures 列表中。

4.2.3 任务提交

调用 execute 方法将每个 RunnableFuture 提交给线程池执行

4.2.4 等待任务完成

遍历 futures 列表,调用 Future.get() 方法等待每个任务完成。如果任务被取消或抛出异常,则忽略这些异常。

4.2.5 异常处理和任务取消

如果在等待任务完成期间抛出异常,则取消所有未完成的任务

4.3 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 的 invokeAll 方法实现

ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 直接继承了 AbstractExecutorService 中的 invokeAll 方法,因此它们的 invokeAll 方法实现逻辑与 AbstractExecutorService 中的实现相同。

5. invokeAny()的底层实现

在这里插入图片描述
ExecutorService 接口的 invokeAny 方法用于批量提交一组 Callable 任务,并返回其中一个成功完成的任务的结果。与 invokeAll 不同的是,invokeAny 只需要等待其中一个任务成功完成即可。

5.1 ExecutorService 接口中的 invokeAny 方法

ExecutorService 接口定义了两个 invokeAny 方法:

<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;

<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

5.2 AbstractExecutorService 中的 invokeAny 方法

AbstractExecutorService 提供了 invokeAny 方法的默认实现:

public abstract class AbstractExecutorService implements ExecutorService {

    public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
        try {
            return doInvokeAny(tasks, false, 0);
        } catch (TimeoutException cannotHappen) {
            assert false;
            return null;
        }
    }

    public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        return doInvokeAny(tasks, true, unit.toNanos(timeout));
    }

    private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException {
        if (tasks == null) throw new NullPointerException();
        int ntasks = tasks.size();
        if (ntasks == 0) throw new IllegalArgumentException();
        
        ArrayList<Future<T>> futures = new ArrayList<>(ntasks);
        ExecutorCompletionService<T> ecs = new ExecutorCompletionService<>(this);
        
        try {
            ExecutionException ee = null;
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            Iterator<? extends Callable<T>> it = tasks.iterator();

            futures.add(ecs.submit(it.next()));
            --ntasks;
            int active = 1;
            
            for (;;) {
                Future<T> f = ecs.poll();
                if (f == null) {
                    if (ntasks > 0) {
                        --ntasks;
                        futures.add(ecs.submit(it.next()));
                        ++active;
                    } else if (active == 0)
                        break;
                    else if (timed) {
                        f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
                        if (f == null)
                            throw new TimeoutException();
                        nanos = deadline - System.nanoTime();
                    } else
                        f = ecs.take();
                }
                if (f != null) {
                    --active;
                    try {
                        return f.get();
                    } catch (ExecutionException eex) {
                        ee = eex;
                    } catch (RuntimeException rex) {
                        ee = new ExecutionException(rex);
                    }
                }
            }

            if (ee == null)
                ee = new ExecutionException();
            throw ee;

        } finally {
            for (Future<T> f : futures)
                f.cancel(true);
        }
    }
}

invokeAny(Collection<? extends Callable> tasks)主要步骤解析

  1. 参数检查
  • 检查传入的任务集合是否为 null,如果是则抛出 NullPointerException。
  • 检查任务集合是否为空,如果是则抛出 IllegalArgumentException。
  1. 任务提交和执行
  • 创建一个 ExecutorCompletionService 实例,用于管理任务的提交和结果的获取。
  • 提交第一个任务,并将其 Future 对象添加到 futures 列表中。
  • 使用循环依次提交剩余的任务,并在 ExecutorCompletionService 中等待第一个完成的任务。
  1. 任务结果获取
  • 使用 ecs.poll() 或 ecs.take() 方法获取已完成的任务。
  • 如果任务成功完成,则返回其结果。
  • 如果任务抛出异常,则记录异常并继续等待其他任务完成。
  1. 异常处理和任务取消
  • 如果所有任务都抛出异常,则抛出最后一个异常
  • 在方法结束时,取消所有未完成的任务

invokeAny(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)主要步骤解析

  1. 参数检查
  • 检查传入的任务集合和时间单位是否为 null,如果是则抛出 NullPointerException。
  • 检查任务集合是否为空,如果是则抛出 IllegalArgumentException。
  1. 任务提交和执行
  • 与无超时版本相同,创建一个 ExecutorCompletionService 实例,并提交第一个任务。
  • 计算截止时间 deadline。
  1. 任务结果获取
  • 使用 ecs.poll(nanos, TimeUnit.NANOSECONDS) 方法在指定时间内等待任务完成。
  • 如果任务在超时时间内完成,则返回其结果。
  • 如果超时,则抛出 TimeoutException。
  1. 异常处理和任务取消
    无超时版本相同,处理任务抛出的异常,并在方法结束时取消所有未完成的任务。

5.3 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 的 invokeAny 方法实现

ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 直接继承了 AbstractExecutorService 中的 invokeAny 方法,因此它们的 invokeAny 方法实现逻辑与 AbstractExecutorService 中的实现相同。

系列文章

1.JDK源码阅读之环境搭建

2.JDK源码阅读之目录介绍

3.jdk源码阅读之ArrayList(上)

4.jdk源码阅读之ArrayList(下)

5.jdk源码阅读之HashMap

6.jdk源码阅读之HashMap(下)

7.jdk源码阅读之ConcurrentHashMap(上)

8.jdk源码阅读之ConcurrentHashMap(下)

9.jdk源码阅读之ThreadLocal

10.jdk源码阅读之ReentrantLock

11.jdk源码阅读之CountDownLatch

12.jdk源码阅读之CyclicBarrier

13.jdk源码阅读之Semaphore

14.jdk源码阅读之线程池(上)

15.jdk源码阅读之线程池(下)

16.jdk源码阅读之ArrayBlockingQueue

17.jdk源码阅读之LinkedBlockingQueue

18.jdk源码阅读之CopyOnWriteArrayList

19.jdk源码阅读之FutureTask

20.jdk源码阅读之CompletableFuture

21.jdk源码阅读之AtomicLong

22.jdk源码阅读之Thread(上)

23.jdk源码阅读之Thread(下)

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

至真源

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值