Java多线程——线程池原理

前言

线程是稀缺资源,如果被无限制地创建,不仅会消耗系统资源,还会降低系统稳定性。合理地使用线程池对线程进行统一分配、调优和监控,有以下好处:

  • 降低资源消耗
  • 提高响应速度
  • 提高线程的可管理性

Java1.5引入的Executor框架把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池。而不需要关心该任务是如何执行、被哪个线程执行以及什么时候执行。

线程池的使用

步骤如下:

  1. 使用Executors类提供的工厂方法,创建所需要的线程池
  2. 继承Thread或实现Runnable,创建Task类
  3. 向线程池中添加Task,启动任务

实例代码:

    private static Executor mExecutor = Executors.newFixedThreadPool(10);

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            mExecutor.execute(new Task());
        }
    }

    static class Task implements Runnable {
        @Override
        public void run() {
            LogUtils.e("Current Thread is named —— " + Thread.currentThread().getName());
        }
    }

线程池原理知识

Executor框架的两级调度模型

在Java线程启动时,会创建一个本地操作系统线程,当该Java线程终止时,这个操作系统线程也会被回收。而每一个Java线程都会被一对一映射为本地操作系统的线程,操作系统会调度所有的线程并将它们分别给可用的CPU。而所谓的映射方式是这样实现的。在上层,Java多线程程序通过把应用分为若干个任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。这样两级调度模型如下图所示:

Java线程两级调度模型

从图中我们可以看出,应用程序通过Executor框架控制上层的调度,而下层的调度由操作系统内核控制,下层的调度不受应用程序控制。

Executor框架的结构

Executor框架的结构主要包括3个部分:

  1. 任务(Task):被执行任务需要执行的接口——Runnable接口或Callable接口。
  2. 任务的执行:包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。Executor框架中有两个关键类实现了ExecutorService接口——ThreadPoolExecutor和ScheduledThreadPoolExecutor(继承了ThreadPoolExecutor)。
  3. 异步运行的结果:包括接口Future和实现了Future接口的FutureTask类(还实现了Runnable接口)。

下面我们通过一个UML图来认识一下这些类之间的关系:

Executor框架的类结构图

Executor是一个接口,它是Executor框架的基础,它将任务的提交和任务的执行分离开来。
ThreadPoolExecutor是线程池的核心实现类,用来执行被提交的任务。
ScheduleThreadPoolExecutor是一个实现类,可以在给定的延迟后运行命令,或者定期执行命令。比Timer更灵活,功能更强大。
Future接口和实现了Future接口的FutureTask类,代表异步计算的结果。
Runnable接口和Callable接口的实现类,都可以被ThreadPoolExecutor或者ScheduleThreadPoolExecutor执行。区别就是Runnable无法返回执行结果,而Callable可以返回执行结果。

下面我们通过一张图来理解它们之间的执行关系:

Executor任务执行关系

分析说明:

  1. 主线程首先创建Runnable或Callable接口的任务对象,工具类Executors可以把一个Runnable对象封装为一个Callable对象,使用以下两种方式:Executors.callable(Runnable task)或者Executors.callable(Runnable task, T result)。
  2. 然后可以把Runnable对象或者Callable对象直接提交给ExecutorService执行,方法为ExecutorService.submit(Runnable task)或ExecutorService.submit(Callable task)。当然,由于FutureTask也实现了Runnable接口,我们也可以直接创建FutureTask,然后提交给ExecutorService执行。

到此Executor框架的主要体系结构我们都介绍完了。接下来介绍其中比较重要的类。

Executors

Executors是java线程池的工厂类,通过它可以快速初始化一个符合业务需求的线程池。

以下为Executors类中用于创建不同线程池的常用工厂方法:

newFixedThreadPool(int nThreads)

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

初始化一个指定线程数的线程池,使用LinkedBlockingQueue作为阻塞队列,当没有空闲线程时,后面提交的任务将加入LinkedBlockingQueue。由于LinkedBlockingQueue是无界队列,同时corePoolSize == maximumPoolSize,所以参数maximumPoolSize和keepAliveTime无效,运行时也不会拒绝任务,可以一直添加新任务到阻塞队列中,不会调用RejectExecutionHandler的rejectExecution方法抛出异常。不过当线程没有可执行任务时,也不会释放线程。

FixedThreadPool的execute()方法的执行流程

newCachedThreadPoolr()

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  1. 初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列
  2. 和newFixedThreadPool()创建的线程池不同,newCacheThreadPool()在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源
  3. 因为使用的是没有容量的SynchronousQueue作为线程池的工作队列,但其maximumPoolSize是无界的,当提交新任务时,如果没有空闲线程,则创建新线程执行任务,这样会导致创建过多线程而耗尽CPU和内存资源。

所以,使用该线程池时,一定要注意控制并发的任务数,否则创建大量的线程可能导致严重的性能问题。

CacheThreadPool的execute()方法的执行过程

newSingleThreadExecutor()

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

初始化后的线程池只有一个线程,除了这个其他与FixedThreadPoolExecutor相同。如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行,内部使用LinkedBlockingQueue作为阻塞队列,会按照提交的顺序保存在队列中。这种特点可以用来处理共享资源的问题而不需要考虑同步的问题。

SingleThreadPool的execute()方法的执行过程

newScheduledThreadPool(int corePoolSize)

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

因为ScheduleThreadPoolExecutor继承了ThreadPoolExecutor,所以实际上调用的也是ThreadPoolExecutor的实现方法。

初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。

其本质上都是通过不同的参数初始化一个ExecutorService对象,newScheduledThreadPool()则为ScheduledExecutorService(这个会在后续讲解),其也是继承于ThreadPoolExecutor。

各自的适用场景

  • FixedThreadPool:适用于为了满足资源管理需求,而需要限制当前线程的数量的应用场景,它适用于负载比较重的服务器。
  • CacheThreadPool:大小无界的线程池,适用于执行很多的短期异步任务的小程序,或者负载较轻的服务器。
  • SingleThreadPool:适用于需要保证任务顺序执行;并且在任意时间点,不会有多个线程活动的场景。
  • ScheduleThreadPool:

ExecutorService

public interface ExecutorService extends Executor {

    void shutdown();

    List<Runnable> shutdownNow();

    boolean isShutdown();

    <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;
    ......
}

ExecutorService接口继承了Executor,并且提供了一些其他的方法,比如说:

  • shutdownNow():关闭线程池,返回放入了线程池,但是还没开始执行的任务
  • submit(Callable<T>):执行任务,并且有返回值
  • invokeAll(Collection<? extends Callable<T>>):运行把任务放进集合中,进行批量的执行,并且能有返回值

这三个方法也可以说是这个接口重点扩展的方法。

另外还有shutdown()(关闭执行的任务,并且不接受新任务)、submit(Runnable, T)等方法。

PS:execute()和submit()的区别:

  • submit 有返回值,execute没有返回值。
  • submit 有方便异常的处理方式。如果任务可能抛出异常,而且希望外面的调用者可以感知这些异常,就需要调用submit()方法,捕获Future.get()方法抛出的异常。

AbstractExecutorService

AbstractExecutorService是一个抽象类,主要完成了对ExecutorService接口中submit(),invokeAll()方法的实现。但是它们内部还是调用的execute()方法。

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

    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<Future<T>>(tasks.size());
        boolean done = false;
        try {
            for (Callable<T> t : tasks) {
                RunnableFuture<T> f = newTaskFor(t);
                futures.add(f);
                execute(f);
            }
            for (int i = 0, size = futures.size(); i < size; i++) {
                Future<T> f = futures.get(i);
                if (!f.isDone()) {
                    try {
                        f.get();
                    } catch (CancellationException ignore) {
                    } catch (ExecutionException ignore) {
                    }
                }
            }
            done = true;
            return futures;
        } finally {
            if (!done)
                for (int i = 0, size = futures.size(); i < size; i++)
                    futures.get(i).cancel(true);
        }
    }   

ThreadPoolExecutor

ThreadPoolExecutor继承了AbstractExecutorService,并且实现了最重要的execute()方法,也就是说在AbstractExecutorService中的submit()和invokeAll()方法调用的就是ThreadPoolExecutor中的execute()。

ThreadPoolExecutor的构造方法如下:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

下面对具体参数作介绍:

corePoolSize

线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务会被保存到阻塞队列中,等待被执行;如果执行了线程池的prestartCoreThread()(或prestartAllcoreThreads())方法,线程池会提前创建并启动(所有)核心线程。

maximumPoolSize

线程池中允许启动的最大线程数量。在核心线程全部启动的情况下,如果当前的阻塞队列满了,这时继续提交任务,则会创建新的线程执行任务,前提是当前线程数小于maximumPoolSize。

keepAliveTime

线程在空闲时的存活时间,即当线程没有任务可执行时,所能存活的时间;默认情况下,该参数只在线程数大于corePoolSize才起效。

unit

keepAliveTime的时间单位

workQueue

用来保存等待被执行的任务的阻塞队列,需要保证任务实现了Runnable接口。在JDK中提供了如下阻塞队列:

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
  2. LinkedBlockingQueue:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQueue;
  3. SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue;
  4. PriorityBlockingQueue:具有优先级的无界阻塞队列。

threadFactory

创建线程的工厂,通过自定义的线程工厂可以给每个新建的线程设置一个具有识别度的线程名。

JDK中提供了一个默认的threadFactory:

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

handler(RejectedExecutionHandler)

线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:

  1. AbortPolicy:直接抛出异常,默认策略
  2. CallerRunsPolicy:用调用者所在的线程来执行任务
  3. DiscardOldestPolicy:丢弃阻塞队列中最靠前的任务,并执行当前任务
  4. DiscardPolicy:直接丢弃任务

当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

任务(workers)和任务队列(workQueue)

在这个类中,有两个成员变量非常重要:

private final BlockingQueue<Runnable> workQueue;  
private final HashSet<Worker> workers = new HashSet<Worker>();

对于workers变量,存放了线程对象Worker,Worker实现了Runnable接口,而对于workQueue变量,主要存放了需要执行的任务。这样其实可以猜到,线程池的实现原理应该是从workQueue中不断地取出需要执行的任务,加入到workers中进行处理。

线程池内部状态

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

    /*
     * Bit field accessors that don't require unpacking ctl.
     * These depend on the bit layout and on workerCount being never negative.
     */

    private static boolean runStateLessThan(int c, int s) {
        return c < s;
    }

    private static boolean runStateAtLeast(int c, int s) {
        return c >= s;
    }

    private static boolean isRunning(int c) {
        return c < SHUTDOWN;
    }

其中AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:
1. RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
2. SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
3. STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
4. TIDYING : 2 << COUNT_BITS,即高3位为010;
5. TERMINATED: 3 << COUNT_BITS,即高3位为011;

另外,当线程池中的线程都处于运行状态,多余的任务会等待,那么等待的过程是怎样实现的呢?答案是利用了BlockingQueue的take()方法进行处理。

任务提交

线程池框架提供了两种提交任务的方式,根据不同的业务需求选择不同的方式。

Executor.execute()

public interface Executor {

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the {@code Executor} implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
}

通过Executor.execute()方法提交的任务,必须实现Runnable接口,该方式提交的任务不能获取返回值,因此无法判断任务是否执行成功。

ExecutorService.submit()

    <T> Future<T> submit(Callable<T> task);

    <T> Future<T> submit(Runnable task, T result);

    Future<?> submit(Runnable task);

通过ExecutorService.submit()方法提交的任务,可以获取任务的返回值。

事实上,无论是Executor.execute()或是ExecutorService.submit()方法,在它们内部实现线程任务执行的方法是同一个,即继承了AbstractExecutorService的ThreadPoolExecutor中的execute()方法。接下来就将介绍这个方法。

execute()

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

这里的代码逻辑在代码注释中解释的很清楚了。为了方便阅读,我还是对其进行翻译一下:

  1. workCountOf(c)方法根据ctl的低29位,得到当前线程池的当前线程数,如果正在运行的线程数小于corePoolSize,则尝试调用addWorker()创建新的线程来执行任务。否则执行步骤2。
  2. 如果线程池处于RUNNING状态,且把提交的任务成功放入阻塞队列中,则执行步骤3;否则执行步骤5
  3. 再次检查线程池的状态,如果不是处于RUNNING状态,且能够成功从阻塞队列中移除任务,则执行reject方法处理任务;否则,执行步骤4
  4. 如果此时线程池中线程数为0,则调用addWorker()处理任务
  5. 执行addWorker()方法创建新的线程执行任务,如果addWorker()失败,则使用reject()处理任务

execute()执行过程

addWorker()

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }   

这部分是addWorker()的前半部分
1. 判断线程池的状态,如果状态值大于或等于SHUTDOWN,则不处理提交的任务,直接返回;否则,进入步骤2
2. 通过参数core判断当前需要创建的线程是否为核心线程,如果为true,且当前线程数小于corePoolSize,则跳出循环,开始创建新的线程,具体实现如下:

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    // Recheck while holding lock.
                    // Back out on ThreadFactory failure or if
                    // shut down before lock acquired.
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // precheck that t is startable
                            throw new IllegalThreadStateException();
                        workers.add(w);
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

线程池的工作线程通过Worker类实现,在ReentrantLock锁的保证下,把Worker实例插入到HashSet后,启动Worker中的线程,其中Worker类设计如下:

  1. 继承了AQS类(AbstractQueuedSynchronizer),可以方便的实现工作线程的中止操作;
  2. 实现了Runnable接口,可以将自身作为一个任务在工作线程中执行
  3. 当前提交的任务firstTask作为参数传入Worker的构造方法
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

        /** Delegates main run loop to outer runWorker. */
        public void run() {
            runWorker(this);
        }

从Worker的构造方法中可以看到:线程工厂在创建线程thread时,将Worker实例本身this作为参数传入,当执行start()方法启动线程thread时,本质是执行了Worker的runWorker()方法。

runWorker()

    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // If pool is stopping, ensure thread is interrupted;
                // if not, ensure thread is not interrupted.  This
                // requires a recheck in second case to deal with
                // shutdownNow race while clearing interrupt
                if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();
                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    。。。。。。
                    } finally {
                        afterExecute(task, thrown);
                    }
                。。。。。。

runWorker()是线程池的核心:

  1. 线程启动之后,通过unlock()方法释放锁,设置AQS的state为0(调用了release(1)),表示运行中断
  2. 获取第一个任务firstTask,执行任务的run()方法,不过在执行任务之前,会进行加锁操作,任务执行完会释放锁
  3. 在执行任务的前后,可以根据业务场景自定义beforeExecute()和afterExecute()
  4. firstTask执行完成之后,通过getTask()方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用CPU资源
  5. 如果task不为空,则开始执行任务,先通过lock()方法加锁,判定线程是否正常运行,如果是,就执行task.run()方法。

getTask()

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

整个getTask()操作在自循环中完成,主要操作是:

  1. workQueue.poll():如果在keepAliveTime时间内,阻塞队列还是没有任务,则返回null;
  2. workQueue.take():如果阻塞队列为空,当前线程会挂起等待;当队列中有任务加入时,线程被唤醒,take()方法返回任务并执行

ScheduleThreadPoolExecutor

ScheduleThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后执行任务,或者定期执行任务。功能与Timer类似,但比Timer更强大,Timer对应的是单个后台线程,而ScheduleThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。

执行机制分析

ScheduleThreadPoolExecutor的执行过程

ScheduleThreadPoolExecutor使用了DelayQueue作为阻塞队列,和LinkedBlockingQueue一样,是无界队列。ScheduleThreadPoolExecutor的初始化和FixedThreadPoolExecutor等一样,实质上都是调用了ThreadPoolExecutor的初始化方法。不同的是,当调用scheduleAtFixedRate()或scheduleWithFixedDelay()方法时,会向ScheduleThreadPoolExecutor的DelayQueue添加一个实现了RunnableScheduleFuture接口的ScheduleFutureTask。

使用实例

    public static void startExecutor(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(10);
        LogUtils.e("startTime = " + getFormatTime());
        try {
            for (int i = 0; i < 3; i++) {
//                Worker worker = new Worker();
                Task task = new Task();
                executorService.schedule(task, 10, TimeUnit.SECONDS);
                Thread.sleep(1000);
            }
            Thread.sleep(3000);
        } catch (InterruptedException e) {
                e.printStackTrace();
//            executorService.scheduleAtFixedRate();
//            executorService.scheduleWithFixedDelay();
        }
        executorService.shutdown();

        while (!executorService.isTerminated()) {
            //wait for all worker to be finished
        }

        LogUtils.e("All workers is finished, the time = " + getFormatTime());
    }

运行结果:

schedule()方法的运行结果

线程任务确实在延迟10s后才开始执行。这就时schedule()方法的使用。

scheduleAtFixedRate()

scheduleAtFixedRate()方法的作用是预定在设定的initialDelay时间结束后,周期地执行给定的任务。周期长度为period。

    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (period <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(period));
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }
scheduleWithFixedDelay()

scheduleWithFixedDelay()方法的作用是预定在初始的延迟结束后周期地执行给定任务,不同的是在一次调用完成和下一次调用开始之间有长度为delay的延迟。

    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit) {
        if (command == null || unit == null)
            throw new NullPointerException();
        if (delay <= 0)
            throw new IllegalArgumentException();
        ScheduledFutureTask<Void> sft =
            new ScheduledFutureTask<Void>(command,
                                          null,
                                          triggerTime(initialDelay, unit),
                                          unit.toNanos(-delay));  //唯一和scheduleAtFixedRate()不同的地方
        RunnableScheduledFuture<Void> t = decorateTask(command, sft);
        sft.outerTask = t;
        delayedExecute(t);
        return t;
    }

Future和Callable实现

通过ExecutorService.submit()方法提交的任务,可以获取任务执行完的返回值

    private static ExecutorService mExecutor = Executors.newFixedThreadPool(10);

    public void startForResult() {
        Future future = mExecutor.submit(new Task());
        try {
            String result = (String) future.get();
            LogUtils.e("result = " + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }

   static class Task implements Callable {
        @Override
        public Object call() throws Exception {
            TimeUnit.SECONDS.sleep(2);
            return "this is a future case";
        }
    }

在实际业务场景中,Future和Callable基本是成对出现的,Callable负责产生结果,Future负责获取结果。

  1. Callable接口类似于Runnable接口,只是Runnable没有返回值,使用Runnable作为参数传入时,会被包装成Callable
  2. Callable任务除了返回正常结果之外,如果发生异常,该异常也会被返回,即Future可以拿到异步执行任务的各种结果
  3. Future.get()方法会导致主线程阻塞,知道Callable任务执行完成

submit()

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

通过submit()方法提交的Callable任务会被封装成一个FutureTask对象

FutureTask

 * Possible state transitions:
     * NEW -> COMPLETING -> NORMAL
     * NEW -> COMPLETING -> EXCEPTIONAL
     * NEW -> CANCELLED
     * NEW -> INTERRUPTING -> INTERRUPTED
     */
    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;

FutureTask在不同阶段拥有不同的状态state,初始值为NEW(0)。
FutureTask类实现了Runnable接口,这样就可以通过Executor.execute()方法提交FutureTask到线程池中等待被执行,最终执行了FutureTask的run()方法,保存运行结果。

FutureTask.get()

    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

内部通过awaitDone()方法对主线程进行阻塞,具体实现如下:

    private int awaitDone(boolean timed, long nanos)
        throws InterruptedException {
        final long deadline = timed ? System.nanoTime() + nanos : 0L;
        WaitNode q = null;
        boolean queued = false;
        for (;;) {
            if (Thread.interrupted()) {
                removeWaiter(q);
                throw new InterruptedException();
            }

            int s = state;
            if (s > COMPLETING) {
                if (q != null)
                    q.thread = null;
                return s;
            }
            else if (s == COMPLETING) // cannot time out yet
                Thread.yield();
            else if (q == null)
                q = new WaitNode();
            else if (!queued)
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                     q.next = waiters, q);
            else if (timed) {
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            }
            else
                LockSupport.park(this);
        }
    }
  1. 如果主线程被中断,则抛出中断异常
  2. 判断FutureTask当前的state,如果大于COMPLETING,说明任务已经执行完成,则直接返回
  3. 如果等于COMPLETING,说明任务执行完成,这时主线程只需通过yield()方法让出CPU资源,等待state变成NORMAL
  4. 通过WaitNode类封装当前线程,并通过UNSAFE(unsafe mechanics)添加到waiters链表
  5. 最终通过LockSupport的park或parkNanos挂起线程

FutureTask.run()

    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }

FutureTask.run()方法是在线程池中被执行的,而非主线程:

  1. 通过执行Callable任务的call()方法
  2. 如果call()执行成功,则通过set(result)方法保存结果
  3. 如果call()执行出现异常,则通过setException()保存异常

set()

    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

setException()

    protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

set和setException方法中,都会通过UnSAFE修改FutureTask的状态,并执行finishCompleting()方法通知主线程任务已经执行完成

finishCompleting()

    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                for (;;) {
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }

        done();

        callable = null;        // to reduce footprint
    }
  1. 执行FutureTask类的get()方法时,会把主线程封装成WaitNode结点并保存在waiters链表中
  2. FutureTask任务执行完成后,通过UNSAFE设置waiters的值,并通过LockSupport.unpark()方法唤醒主线程

深入分析java线程池的实现原理
java&android线程池-Executor框架之ThreadPoolExcutor&ScheduledThreadPoolExecutor浅析(多线程编程之三)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值