读懂Java并发系列——6.线程池源码解读

105 阅读3分钟

1. 线程池概述

线程池将线程的执行和任务相分离,其中任务由Runnable接口和Callable接口及RunnableFuture接口组成。

2. 线程池基础

线程池的顶级接口Executor只有一个方法:execute(),这个方法执行任务。 ExecutorService扩展了Executor,增加了对线程池的控制及增强了线程的执行。 image.png

2.1 ExecutorService方法解析

  1. shutdown和shutdownNow区别及内部原理 shutdown:通知关闭线程池,不再添加任务,并且已经执行任务的线程池继续执行,执行完毕将关闭。 shutdownNow:立即关闭线程池(立即是多久),不再接受新任务,并且已经执行的任务立即停止(已经执行的任务真的会立即停止吗?)
   public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 检查是否有权限关闭线程池
            checkShutdownAccess();
            // 修改线程池状态为SHUTDOWN
            advanceRunState(SHUTDOWN);
            // 标记所有线程为中断状态
            interruptIdleWorkers();
            onShutdown(); // 没用,这个方法没有实现,保留目的是为了执行一些清理工作
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

为什么通过shutdown()方法就可以做到不再添加任务,并且已经执行任务的线程池继续执行,执行完毕将关闭? 首先:不再添加新的任务,是通过在addWorker方法中检查线程池状态, interruptIdleWorkers将所有空闲的线程标记为中断状态,并且runWorker()方法中调用的getTask()因为检查了线程池状态,将不会再获取到新的任务。所有线程执行完毕后关闭。

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

探究这个问题,可以看看一个任务是如何被执行的。

 public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 将状态标记为STOP
            advanceRunState(STOP);
            // 标记中断所有线程
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }
  1. submit方法的三种不同的区别

submit三种方法的不同取决于参数的类型和是否有返回值,其中submit(Callable call)有返回值,submit(Runnable run)是否有返回值取决于Runnable,如果是RunnableFuture则有返回值。

  1. invokeAll方法内部实现原理 invokeAll代表批量提交执行任务,实际此方法并不实用。
 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);
        }
    }

3. 线程池问题思考

  • 线程池如何复用线程? 线程池复用线程的关键:线程池将每个线程包装成Worker类,Worker类继承AQS,在创建Worker时一般会指定第一个任务,后序通过循环getTask()获取任务,获取不到时根据策略关闭线程
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;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
            processWorkerExit(w, completedAbruptly);
        }
    }
  • 线程池的种类 线程池加入任务的逻辑: 1.核心线程->加入阻塞队列->创建非核心线程 核心线程根据allowCoreThreadTimeOut可以设置是否关闭超时关闭。
  • 线程池高级用法
  1. 查看活跃线程数:getActiveCount()
  2. 查看总的线程数
  3. 查看任务总数:getTaskCount()
  4. 查看完成的任务总数:getCompletedTaskCount()