• JUC 之 ThreadPoolExecutor 的一些研究


    ThreadPoolExecutor 概述:=====================================================================

    构造函数:

    4个构造函数, 其实最终都是调用了这个:

        /**
         * Creates a new {@code ThreadPoolExecutor} with the given initial
         * parameters.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @param maximumPoolSize the maximum number of threads to allow in the
         *        pool
         * @param keepAliveTime when the number of threads is greater than
         *        the core, this is the maximum time that excess idle threads
         *        will wait for new tasks before terminating.
         * @param unit the time unit for the {@code keepAliveTime} argument
         * @param workQueue the queue to use for holding tasks before they are
         *        executed.  This queue will hold only the {@code Runnable}
         *        tasks submitted by the {@code execute} method.
         * @param threadFactory the factory to use when the executor
         *        creates a new thread
         * @param handler the handler to use when execution is blocked
         *        because the thread bounds and queue capacities are reached
         * @throws IllegalArgumentException if one of the following holds:<br>
         *         {@code corePoolSize < 0}<br>
         *         {@code keepAliveTime < 0}<br>
         *         {@code maximumPoolSize <= 0}<br>
         *         {@code maximumPoolSize < corePoolSize}
         * @throws NullPointerException if {@code workQueue}
         *         or {@code threadFactory} or {@code handler} is null
         */
        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;
        }
        
        其中:
        int corePoolSize,        核心线程数, 个人任务 理解为最小的工作线程数 更好。
        int maximumPoolSize,    最大工作线程数, 也就是最大pool size
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue
        ThreadFactory threadFactory,
        RejectedExecutionHandler handler


    RejectedExecutionHandler 有这么几种:

    AbortPolicy 直接抛出异常
    CallerRunsPolicy 使用用户线程运行, CallerRunsPolicy其实保证了 无论怎么样,即使等待了很长时间,所以的工作任务都会被执行,而绝不会被舍弃。
    DiscardOldestPolicy 舍弃工作的阻塞队列头的工作任务 ,the head of this queue ,然后重新运行当前任务。 也就是舍弃最 先进入队列的任务。 也就是最老的那个。
    DiscardPolicy 直接舍弃,也就是忽略当前添加的工作任务

    注意:
    CallerRunsPolicy 是可能会影响 当前的用户线程的运行的。
    AbortPolicy 可能会抛出给 用户 线程, 故。。 但是, 对之前添加的 Worker 无影响。

    主要的属性或者方法================================================================

    addWorker 方法签名:private boolean addWorker(Runnable firstTask, boolean core) { 尝试为 原始线程 新创建 工作线程。
    addWorkerFailed 新创建 工作线程怎么办?
    advanceRunState
    afterExecute 钩子
    allowCoreThreadTimeOut() 是否允许核心线程超时? 默认是false, 可以通过allowCoreThreadTimeOut方法动态设置
    allowsCoreThreadTimeOut 返回 allowCoreThreadTimeOut
    awaitTermination 签名:public boolean awaitTermination(long timeout, TimeUnit unit) 等待线程池的状态变为TERMINATED,timeout内返回true,否则false。 接下来怎么办, 自己根据返回值去处理。
    beforeExecute 钩子
    checkShutdownAccess
    compareAndDecrementWorkerCount cas方式增加工作线程的数目。一般来说是用在 新建工作线程成功的时候。
    compareAndIncrementWorkerCount cas方式减少工作线程的数目。一般来说是用在 工作线程退出的时候。
    ctlOf
    decrementWorkerCount 无论如何,都要成功减少工作一个线程的数目, 只需一个。
    drainQueue
    ensurePrestart 即使corePoolSize is 0, 也要至少启动一个工作线程。
    execute 关键的工作执行方法
    finalize
    getKeepAliveTime
    interruptIdleWorkers(boolean onlyOne) 中断空闲的worker
    interruptIdleWorkers() 调用前者
    interruptWorkers 中断所有的worker。 Interrupts all threads, even if active. Ignores SecurityExceptions
    isRunning 是否运行?
    isRunningOrShutdown
    onShutdown
    prestartAllCoreThreads 预先启动所有core 线程。 这个方法用的少。 它会导致core 线程idly wait for work,默认是starting core threads only when new tasks are executed
    prestartCoreThread 预先启动一个core 线程。 这个方法也用的少
    processWorkerExit 钩子
    purge
    reject
    remove
    runStateAtLeast
    runStateLessThan
    runStateOf
    runWorker 这个是被 Worker 调用的,是真正的 。。
    setKeepAliveTime
    shutdown 优雅关闭线程池
    shutdownNow 强制关闭线程池
    terminated
    toString
    tryTerminate 尝试Terminate ?
    workerCountOf
    allowCoreThreadTimeOut 是否允许核心线程超时
    CAPACITY final 常量,固定为
    COUNT_BITS
    ctl
    defaultHandler 默认reject handler
    handler
    keepAliveTime core 意外的线程的超时时间。 keepAliveTime其实完全可以理解为 keepAlived timeOut
    mainLock 需要一个 JUC 的Lock, 为什么。
    ONLY_ONE

    // 几种状态
    RUNNING
    SHUTDOWN
    STOP
    TERMINATED
    TIDYING

    termination
    shutdownPerm
    workers 这个很关键, 它线程池是内部的 一个保留Worker 的缓存Set: 签名  private final HashSet<Worker> workers = new HashSet<Worker>();
    workQueue 


    ================================ IDEA 计算出来的属性 ================================
    activeCount: int 实际正在运行 Worker 线程
    completedTaskCount: long 所有总共完成的 "任务"。
    corePoolSize: int 核心的(其实也就是最小的)Worker 线程数
    largestPoolSize: int 曾经达到的最大的 实际运行的Worker 线程数
    maximumPoolSize: int (潜在的)最大的 Worker 线程数
    poolSize: int 当前线程池的 线程数, 不管线程是否正在运行 还是空闲
    public BlockingQueue<Runnable> getQueue() {: BlockingQueue<Runnable> 阻塞队列
    rejectedExecutionHandler: RejectedExecutionHandler 拒绝执行的策略
    shutdown: boolean 是否关闭?
    task: Runnable
    taskCount: long
    terminated: boolean
    terminating: boolean
    threadFactory: ThreadFactory
    ================================ IDEA 计算出来的属性 END ================================

    内部类 Worker:

    它是实际的工作线程:(其实是对原始线程或Runnable的封装)。 Worker 到底是什么东东? 它是这样的:

        private final class Worker
            extends AbstractQueuedSynchronizer
            implements Runnable
            
            Worker
            interruptIfStarted
            lock
            run
            tryAcquire
            tryLock
            tryRelease
            unlock
            completedTasks
            firstTask
            serialVersionUID
            thread
    }


    ThreadPoolExecutor 线程池 的个人理解 =====================================================================

    线程池 最关键的两个属性: 工作线程的Set + 阻塞队列

    private final HashSet<Worker> workers = new HashSet<Worker>(); // workers 的本质是保存所有的实际的工作的线程。

    private final BlockingQueue<Runnable> workQueue; // workQueue 的本质是 对某个时刻 过多的任务 task 做缓冲。( 个人认为 缓冲 比缓存更合适此时的场景)

    线程池本质上是 上面的两个属性:workers + workQueue 和 围绕它们的一些方法。

    所以,JUC线程池,可以简单这么理解:
    用户构造线程池,然后,随意的对线程池发出了各种各样的执行任务的请求,线程池检查 其本身两个属性:workers + workQueue。按照下面的规则处理任务请求:

    1 如果当前pool size < corePoolSize, 则直接添加工作线程 Worker。
    2 如果当前pool size >= corePoolSize,且 < maximumPoolSize,那么使用workQueue 缓冲之。 如果workQueue 无法再缓冲了,那么继续添加Worker
    2 如果当前pool size >= maximumPoolSize,那么使用RejectedExecutionHandler 拒绝之。

    Worker 运行完任务后,不会立即退出,而是立即从workQueue 获取task。获取也要分情况,如果当前pool size <= corePoolSize, 那么一直等待(可理解为阻塞)直到用户有新的工作任务请求发过来。 否则,当然就是pool size > corePoolSize 的情况, 那么就最多等待unit个keepAliveTime的时间,获取到了那么就立即运行,否则 就退出, 也就是工作线程退出,把 自己从workers 移除。

    pool size 就是 当前Worker的总数,包括正在运行的和空闲的。

    注意, 只有工作线程数已经达到了maximumPoolSize,而用户还在添加工作任务时, RejectedExecutionHandler 才会生效。

    workQueue 的 缓冲能力 完全取决于workQueue的capacity,pool 能够同时接受的工作任务 == maximumPoolSize + workQueue的capacity 。
    workers.size 的最大值 = maximumPoolSize;
    也就是 : max pool size = maximumPoolSize + workQueue.size()


    workQueue 的capacity可以是0(比如SynchronousQueue ),也就是 没有缓冲能力。 这个时候, 工作任务会直接被 之前创建的空闲Worker 执行(如果有的话) 或创建新的 Worker 执行。


    keepAliveTime 直接影响了 Worker 的退出。 每个新建Worker 执行完第一个 工作任务后, 会循环去 workQueue 中拿被缓冲的 工作任务, 拿到则执行, 拿不到则等待一个 keepAliveTime 的时间。 所以, 如果 workQueue 中还有工作任务, 那么 Worker 是不会退出的。

    源码分析: =====================================================================


    线程从调用 ThreadPoolExecutor 的submit 方法开始, ThreadPoolExecutor 调用 AbstractExecutorService 的 submit:

        public Future<?> submit(Runnable task) {                    // command 就 工作任务 
            if (task == null) throw new NullPointerException();
            RunnableFuture<Void> ftask = newTaskFor(task, null);        // 创建一个 RunnableFuture
            execute(ftask);
            return ftask;
        }

    然后调用 execute :

        public void execute(Runnable command) {                     // command 的拒绝主要是在这个方法: execute完成的 
            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        // 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();
            
            //  我的理解:  这里应该是规则1, 如果工作线程数小于 核心线程数,直接增加线程。
            if (workerCountOf(c) < corePoolSize) {//工作线程数 < 核心线程数 则 直接添加
                if (addWorker(command, true))      // 成功则直接返回吧,但有可能添加失败,失败也是可能的,比如当前工作线程 == 核心线程数-1,同时有多个工作任务过来。
                    return;
                c = ctl.get();
            }
            
            //  这里应该是规则2, 如果工作线程数大于 核心线程数,小于最大线程数,则直接增加线程
            if (isRunning(c) && workQueue.offer(command)) {    //如果pool还在运行,那么尝试把 command 缓冲到workQueue 起来。 
                int recheck = ctl.get();
                if (! isRunning(recheck) && remove(command))// recheck的意义在于: 虽然workQueue.offer 当前成功了, 但是同时,那个瞬间,pool 状态被改变为SHUTDOWN了,那么根据SHUTDOWN的语义,SHUTDOWN状态下是不应该再继续addWorker, 故先remove,然后再reject这个 command。 
                    reject(command);
                else if (workerCountOf(recheck) == 0)    // 如果还有工作线程,那么就不 addWorker, 因为 Worker 会自动从workQueue中拉取 工作任务; 否则就是表明没有任何工作线程了, 也就是工作线程 数 == 0, 那么 添加工作线程吧。 
                这种情况出现于 之前的工作任务耗时都很短, 很快运行完了, 故 没有进入 if (workerCountOf(c) < corePoolSize) 判断。。 也有可能 corePoolSize,但是workQueue有缓冲能力, 虽然,corePoolSize, 但是至少也是需要一个线程来运行 workQueue中的 工作任务的!。。
                    addWorker(null, false);
            }
            
            //  规则3 体现在哪里呢?  如果工作线程数大于 最大线程数,则reject 
            else if (!addWorker(command, false))// 再次尝试 缓冲到workQueue, 注意这里的第二个参数是false。可能失败(比如workQueue已经满了。), 那么继续添加Worker吧; 
            
                reject(command);                 // 如果工作线程数 >= 最大工作线程数,addWorker 就会成功,否则就失败, 失败则 reject 它
        }
        



    考虑一种情况:
    若workQueue 没有缓冲能力, 而且corePoolSize也是0, 而且maximumPoolSize 也是0。No, 观察构造函数发现: 不可能出现 maximumPoolSize < corePoolSize
    若workQueue 没有缓冲能力, 而且corePoolSize > 0,那么 工作任务其实 直接交给了工作线程处理。。

        private static boolean isRunning(int c) {    // 判断pool 状态是否是 小于SHUTDOWN。
            return c < SHUTDOWN;
        }
        


    addWorker(null, false); 的含义:
    addWorker 成功后是 运行工作线程, 然后调用下面的方法 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) {    // getTask() 方法 会从 workQueue 中窃取工作任务。
                    w.lock();                                         // 为什么这里会有一个 lock ? 据说是为了防止其他pool 执行这个task, 匪夷所思。
                    // 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);
            }
        }
        

    pool 中 线程name 的由来:=====================================================================

    Thread 的 toString 是: 

        public String toString() {
            ThreadGroup group = getThreadGroup();
            if (group != null) {
                return "Thread[" + getName() + "," + getPriority() + "," +
                               group.getName() + "]";
            } else {
                return "Thread[" + getName() + "," + getPriority() + "," +
                                "" + "]";
            }
        }

    而默认的线程工厂类是:

            DefaultThreadFactory() {
                SecurityManager s = System.getSecurityManager();
                group = (s != null) ? s.getThreadGroup() :
                                      Thread.currentThread().getThreadGroup();
                namePrefix = "pool-" +
                              poolNumber.getAndIncrement() +
                             "-thread-";
            }
    
            public Thread newThread(Runnable r) {
                Thread t = new Thread(group, r,
                                      namePrefix + threadNumber.getAndIncrement(),
                                      0);
                if (t.isDaemon())
                    t.setDaemon(false);
                if (t.getPriority() != Thread.NORM_PRIORITY)
                    t.setPriority(Thread.NORM_PRIORITY);
                return t;
            }



    可见,默认情况下,名字是: "pool-" + poolNumber.getAndIncrement() + "-thread-" + threadNumber.getAndIncrement()

    那么pool.submit(thread); 时给 thread 设置的名字是无效的。 因为实际显示的时候, 不会用到,我们仅仅使用 thread 的 run 方法。 其实 pool 需要submit的并不是 Thread ,而是 Runnable 或者 Callable

    pool 工作状态及 生命周期:=====================================================================

    pool 状态有:

    // 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;

    pool 创建好之后,基本就是处于 RUNNING 了。 之后要是不执行 shutdown,shutdownNow, tryTerminate 方法, 它就一直处于 Running 状态。

    shutdown 会优雅的关闭pool,它不限制时间,但最终pool 是会关闭的。
    shutdownNow 立即强制的关闭 pool

    tryTerminate 呢, 只是尝试关闭 pool ,而最终可能对于pool 没有影响?

    AbstractExecutorService 提供了几个 重载的 submit 方法: =======================================

        /**
         * @throws RejectedExecutionException {@inheritDoc}
         * @throws NullPointerException       {@inheritDoc}
         */
        public Future<?> submit(Runnable task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<Void> ftask = newTaskFor(task, null);
            execute(ftask);
            return ftask;
        }
    
        /**
         * @throws RejectedExecutionException {@inheritDoc}
         * @throws NullPointerException       {@inheritDoc}
         */
        public <T> Future<T> submit(Runnable task, T result) {        // 对于这个方法, 返回值其实执行run 之前就已经固定了为 result 
            if (task == null) throw new NullPointerException();
            RunnableFuture<T> ftask = newTaskFor(task, result);
            execute(ftask);
            return ftask;
        }
    
        /**
         * @throws RejectedExecutionException {@inheritDoc}
         * @throws NullPointerException       {@inheritDoc}
         */
        public <T> Future<T> submit(Callable<T> task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<T> ftask = newTaskFor(task);
            execute(ftask);
            return ftask;
        }

    可见: submit 方法都是有返回值的, 当然, 对于Runnable 来说, 返回值只能是 null, 对于 Callable来说, 返回值类型是 其中call 的返回值类型。。
    Runnable 其实也会被适配到 Callable。 实际上运行的是 Callable 的 run 方法。

    submit 返回了一个Future 对象, 实际上我们还可以通过Future 对我们之前提交的任务进行一些操作, 比如检查是否执行完毕,取消它,等待执行完毕。

    关于FutureTask =====================================================================

    FutureTask 是RunnableFuture的一个实现:
    public class FutureTask<V> implements RunnableFuture<V> {

    newTaskFor(task); 返回的实际上就是 FutureTask。 FutureTask 对 Callable 和其返回值同时进行了封装,FutureTask运行的是 Callable run 方法。 FutureTask 提供get 方法, 获取Callable 的返回值。

    FutureTask 的 get() 方法不会立即返回,它是异步的, 必须等待 任务运行完成了, 才会有结果,否则阻塞。


    FutureTask 另外提供了一个 可超时的 get 方法: get(long timeout, TimeUnit unit):
    public V get(long timeout, TimeUnit unit)

    FutureTask 有7个状态:
    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;

    pool 的runWorker方法运行的实际是 FutureTask 的run 方法,最终运行 Callable 的run 方法:

    THE END =====================================================================

    参考
    http://blog.csdn.net/vernonzheng/article/details/8299108 等 

  • 相关阅读:
    三元运算,列表解析,生成器表达式
    迭代器补充
    迭代器协议和for循环工作机制
    文件seek方法的补充
    文件操作的其他方法
    文件操作b模式
    文件操作的其他模式
    其他内置函数
    [TimLinux] JavaScript table的td内容超过宽度缩为三个点
    [TimLinux] JavaScript BOM浏览器对象模型
  • 原文地址:https://www.cnblogs.com/FlyAway2013/p/7589635.html
Copyright © 2020-2023  润新知