• Java线程池之Executors.newSingleThreadExecutor()


    Java线程池Executors.newSingleThreadExecutor()

    前言:本文先就Java线程池 ThreadPoolExecutor 进行分析,然后逐步分析单线程池的源码工作流程

    ThreadPoolExecutor的工作流程

    我们执行以下代码:

    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
    singleThreadExecutor.execute(new Runnable() {
        @Override
        public void run() {
            ...
        }
    });
    

    当线程池提交Runnable实现时singleThreadExecutor.execute(Runnable command) ,其大致的工作流程如下:

    下面跟随源码进入到ThreadPoolExecutor中进行详细分析

    线程池初始化

    Executors.newSingleThreadExecutor()执行时,其内部源码如下:

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

    继续深入new ThreadPoolExecutor方法可以发现,对应的参数的意义分别是:

    public ThreadPoolExecutor(int corePoolSize, // 核心线程数
                              int maximumPoolSize, // 最大线程数
                              long keepAliveTime, // 保活时间(这里是0,单线程池暂时用不上此参数)
                              TimeUnit unit, // 保活时间单位
                              BlockingQueue<Runnable> workQueue // 存放Runnable实现的队列) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    

    往后执行,也就是线程池执行初始化的一个过程,给线程池对象的重要参数进行赋值。

    线程池提交任务

    先看看提交时,最外层的代码:

    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();
        // 1.
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 2.
        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);
        }
        // 3.
        else if (!addWorker(command, false))
            reject(command);
    }
    

    翻译翻译Doug Lea大神写的注释:

    1. 首先判断Worker的数量是否小于corePoolSize核心线程池数量,小于则new Worker对象,启动线程并以此次的Runnable任务为第一次任务进行run()执行。后一段则说有原子性的检查机制保证Worker线程不会额外创建,否则会返回false
    2. 上述条件不满足时,会将Runnale任务放入队列,如果放置成功,且会进行双重校验:先判断线程池是否是SHUTDOWN状态,然后尝试撤回Runnable任务,如果失败,则判断Worker的数量是否为0,如果为0,则新增一个非核心线程池的Worker。根据鄙人目前的理解,这里应该是避免线程池执行shutdown()方法,然后导致Runnable任务没有被正确的处理。
    3. 如果Runnable放入队列失败,则添加非核心线程池的Worker。PS:LinkedBlockingQueue无论如何都可以offer()成功,SynchronousQueueoffer()是返回的false。上述两个BlockingQueue分别对应固定线程数的线程池和非固定线程数的线程池,分别对应的典型是Executors.newSingleThreadExecutor()Executors.newCachedThreadPool()非固定线程数的线程池放在下一期讲。

    addWorker方法与Worker类

    单线程的线程池的提交任务的时候,最重要的就2个步骤:addWorker(command, true)添加核心线程Worker与 workQueue.offer(command)将超出核心线程数的任务放入队列中。

    那么addWorker方法里面发生了什么呢,我摘取了我认为最重要的一段:上锁、new Worker对象与启动线程

    private boolean addWorker(Runnable firstTask, boolean core) {
        ...
        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究竟是怎么执行的呢,调用t.start()发生了什么呢。先看看Worker的类结构:

    再看看Worker的成员变量有哪些,参考其构造方法:

    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }
    

    接下来就是我认为关于Worker最重要核心的地方了,通过上述代码我们可以发现,Worker实现了Runnable接口,然后在构建Worker对象的时候,初始化了它的成员变量thread,并且是以Woker本身赋值进去的!!!看到这里,我们就知道了,t.start()的时候,是执行的Worker.run()方法。


    下面是Worker.run()方法内部的部分源码:

    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 interr
                // if not, ensure thread is not interrupted.  T
                // requires a recheck in second case to deal wi
                // 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);
        }
    }
    

    也就是说,启动Worker的之后,就是循环getTask()task.run()这个getTask()得到的task才是提交到线程池中的Runnable实现
    对于单线程池,getTask()最终执行的是LinkedBlockingQueue.take()方法:

    • 按照队列的FIFO原则(先进先出),取出头部节点
    • 如果没有头部节点,则调用notEmpty.await();方法等待,等待offer()方法时,调用notEmpty.signal();,这里涉及多线程的其它知识了,可以参考并发包中的AbstractQueuedSynchronizer相关知识点。

    至此,单线程池的执行过程大概讲解了一通,现在再来看开篇的工作流程图,又有了更深地理解。

  • 相关阅读:
    CCF CSP 题解
    CCF CSP 2019032 二十四点
    CCF CSP 2018121 小明上学
    CCF CSP 2019092 小明种苹果(续)
    CCF CSP 2019091 小明种苹果
    CCF CSP 2019121 报数
    CCF CSP 2019031 小中大
    CCF CSP 2020061 线性分类器
    CCF CSP 2020062 稀疏向量
    利用国家气象局的webservice查询天气预报(转载)
  • 原文地址:https://www.cnblogs.com/lcmlyj/p/16371148.html
Copyright © 2020-2023  润新知