JAVA线程池源码深度解析

目录

JAVA线程池

ExecutorService

AbstractExecutorService

ThreadPoolExecutor

总结


今天我们来说一说java编程中一个非常重要的工具:java线程池,顾名思义,线程池就是存放线程的池子。要使用线程的时候从池子里面拿出来,不用了再放回去。接下来我们将从源码的角度一步步分析java线程池的奥义。

JAVA线程池

先来看一下java线程池(ThreadPoolExecutor)大致的一个类图

可以看到ThreadPoolExecutor的父接口有ExecutorExecutorService,其中ExecutorService是对Executor接口的扩展,AbstractExecutorServiceThreadPoolExecutor父类,这个类中也实现一些非常重要的方法。需要重点分析的还是ThreadPoolExecutorService,我们通常会使用如下代码来创建java线程池

    public void commit() throws InterruptedException {
     
        ExecutorService executor = new ThreadPoolExecutor(10, 20, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(50));
        executor.execute(new Runnable() {
            @Override
            public void run() {
                // do something...
            }
        });
    }

我们来看一下顶层接口Executor

public interface Executor {

    void execute(Runnable command);
}

里面只有一个方法execute,是java线程池最核心的方法。返回类型是void,这个方法入参是一个runnable的实例,代表提交一个任务。

ExecutorService

ExecutorService是对Executor接口的扩展,这个接口中提供了更加丰富的功能。我们常用的也是这个接口。

public interface ExecutorService extends Executor {

    /**
     * 关闭线程池,不接受新的任务。但是这个方法并不会终止正在执行的任务
     */
    void shutdown();

    /**
     * 也是关闭线程池,不接受新的任务。但是这个方法会停止所有正在执行的任务
     */
    List<Runnable> shutdownNow();

    /**
     * 判断线程池是否已经被关闭
     */
    boolean isShutdown();

    /**
     * 调用shutDown或者shutDownNow后,如果所有任务都完成了,返回true,否则返回false
     */
    boolean isTerminated();

    /**
     * 调用这个方法会等待所有任务完成,并且指定了等待的超时时间,返回是否等待超时。
     * 不论是调用shutDown还是shutDownNow,都不会等待任务结束。
     * 所以我们应该是先调用shutDown/shutDownNow,再调用此方法等待所有任务完成
     */
    boolean awaitTermination(long timeout, TimeUnit unit)
        throws InterruptedException;

    /**
     * 提交一个任务,不同于execute方法,这里的入参是callable,并且返回值是Future
     */
    <T> Future<T> submit(Callable<T> task);

    /**
     * 提交一个任务,但是这里的任务是runnable,
     * runnable执行run后是无返回值的,所以这里传入的result直接作为返回值。
     */
    <T> Future<T> submit(Runnable task, T result);

    /**
     * 提交一个runnable任务
     */
    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;
}

从这里我们看到,线程池提交任务的方法有execute和submit,submit和execute最大区别在于submit方法可以获取到线程的执行结果。这里涉及到一个很重要的类:FutureTask,我们下面来简单看下FutureTask的类图

我们看到FutureTask实现了RunnableFuture,而RunnableFuture继承了Runnable和Future,再来看看FutureTask类中的重要属性和构造方法

public class FutureTask<V> implements RunnableFuture<V> {
    /**
     * 当前任务的执行状态,有以下七种,初始状态为NEW
     */
    private volatile int state;
    private static final int NEW          = 0;
    private static final int COMPLETING   = 1;
    private static final int NORMAL       = 2;
    private static final int EXCEPTIONAL  = 3;
    private static final int CANCELLED    = 4;
    private static final int INTERRUPTING = 5;
    private static final int INTERRUPTED  = 6;

    /** 这个属性很重要,就是依靠它来执行任务的 */
    private Callable<V> callable;
    /** 调用get时,返回的result*/
    private Object outcome; // non-volatile, protected by state reads/writes
    /** 真正执行callable任务的线程 */
    private volatile Thread runner;

    ......

    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

    public FutureTask(Runnable runnable, V result) {
        // 这里会把runnable 和 result组装成一个callable对象
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
    ......
}

submit方法之所以可以获取线程执行结果,全依赖于callable和FutureTask。FutureTask实现了Runnablerun方法,在run方法中会调用callable.call()获取执行结果,并将执行结果就存储在FutureTask中。

调用submit方法的时候,直接将callable/runnable对象包装成FutureTask,然后调用FutureTask.run()。最终返回FutureTask对象。我们调用FutureTask.get()就可以阻塞获取到线程的执行结果了。

本文不对FutureTask做详细讲解,我们接着看AbstractExecutorService

AbstractExecutorService

AbstractExecutorService类实现了许多父类的方法,供子类使用

public abstract class AbstractExecutorService implements ExecutorService {

    /**
     * 将runnable和value包装成FutureTask,以便获取线程执行结果
     */
    protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

    /**
     * 将callable包装成FutureTask,以便获取线程执行结果
     */
    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

    /**
     * 提交任务,返回Future
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        // 组装FutureTask
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        // 将任务交给子类的execute方法去执行
        execute(ftask);
        return ftask;
    }

    /**
     * 提交任务,返回Future,入参跟上面的不一样
     */
    public <T> Future<T> submit(Runnable task, T result) {
        if (task == null) throw new NullPointerException();
        // 组装FutureTask
        RunnableFuture<T> ftask = newTaskFor(task, result);
        // 将任务交给子类的execute方法去执行
        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;
    }

    /**
     * 将collection中的任务提交给线程池执行,只要有一个任务完成,这个方法就可以返回了
     */
    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();
        // 用Future来包装任务
        ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
        // ExecutorCompletionService这个类的构造方法传入了this作为真正的Executor
        // 这个方法会在每个任务完成后,把被FutureTask包装好的callable任务保存到一个LinkedBlokingQueue中
        ExecutorCompletionService<T> ecs =
            new ExecutorCompletionService<T>(this);

        try {
            // 这里可以学习一下人家的异常处理
            ExecutionException ee = null;
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            // 迭代所有的callable任务
            Iterator<? extends Callable<T>> it = tasks.iterator();

            // 这里调用了ecs.submit提交一个任务给线程池,并且将封装每个任务的FutureTask保存在futures中
            futures.add(ecs.submit(it.next()));
            // 任务数-1
            --ntasks;
            // 从名字都可以看出来,这个代表正在执行的任务数
            int active = 1;

            for (;;) {// 注意:这里是个循环
                // 我们上面说ExecutorCompletionService中有个LinkedBlockingQueue用于保存每个任务的FutureTask。
                // 那么这里调用poll方法尝试将FutureTask取出来,这里的poll方法是非阻塞的
                Future<T> f = ecs.poll();
                // 如果f为null,说明前面提交的任务还没执行完成。
                if (f == null) {
                    // 我们说这个方法只要有一个任务执行完成就返回了,
                    // 现在还没有任务执行完成,那么接着执行后面的任务
                    if (ntasks > 0) {
                        --ntasks;
                        futures.add(ecs.submit(it.next()));
                        ++active;
                    }
                    // 如果这里的active为0,说明所有的任务都执行失败了,从这里break出去
                    else if (active == 0)
                        break;
                    // 进入这个分支说明已经没有任务了并且需要检测超时
                    else if (timed) {
                        // 再poll一次,等待nanos这么久,看有没有任务完成
                        f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
                        // 如果超时,抛出异常跳出循环
                        if (f == null)
                            throw new TimeoutException();
                        nanos = deadline - System.nanoTime();
                    }
                    // 到这一步说明已经没有任务了,也没有设置超时,
                    // 那么调用take方法阻塞,直到有任务执行完成
                    else
                        f = ecs.take();
                }
                // 如果f!=null说明有任务执行完成了
                if (f != null) {
                    // 正在执行的任务数-1
                    --active;
                    try {
        
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值