多线程学习笔记:5.ThreadPoolExecutor执行过程初步了解。

变量ctl实现原理:

ThreadPoolExecutor中使用变量ctl来描述线程池状态,存储的是当前线程池的运行状态和线程池中的线程总数,相关代码如下:    

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    //数值为29,用于区分线程池状态区和线程池总数区
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //用于底层二进制运算的中间变量,为00011111111111111111111111111111,后面29个1.
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // 线程池状态,使用位移运算
    //-1左移29位,最后二进制为11100000000000000000000000000000,后面29个0
    private static final int RUNNING    = -1 << COUNT_BITS;
    //0左移29位,最后二进制为00000000000000000000000000000000,后面29个0
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //1左移29位,最后二进制为00100000000000000000000000000000,后面29个0
    private static final int STOP       =  1 << COUNT_BITS;
    //2左移29位,最后二进制为00200000000000000000000000000000,后面29个0
    private static final int TIDYING    =  2 << COUNT_BITS;
    //3左移29位,最后二进制为00300000000000000000000000000000,后面29个0
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 获取线程池状态,将ctl的后29位变成0
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    //获取线程总数,将ctl前3位转换成0
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    //初始化ctl,将线程池状态和线程总数使用或运算合并到一个整型数据中。
    private static int ctlOf(int rs, int wc) { return rs | wc; }

ThreadPool一共定义了5种线程池状态,各状态含义如下:

状态 解释
RUNNING 运行态,可处理新任务并执行队列中的任务
SHUTDOW 关闭态,不接受新任务,但处理队列中的任务
STOP 停止态,不接受新任务,不处理队列中任务,且打断运行中任务
TIDYING 整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法
TERMINATED 结束态,terminated() 方法已完成


当调用shutdown()方法时,线程池进入SHUTDOW状态,                                                                                                                 当调用shutdownNow()方法时,线程池进入STOP状态。

ctl是一个整型数据,一共占32位。因为线程池一共只有5种状态,可以使用一个3位的二进制数表示,所以使用高位的3位表示线程池状态,剩下29位用来表示线程的个数。在代码中涉及了底层二进制数的运算。线程池状态二进制表示如下:

                                                                                                                                         整个ctl的状态,会在线程池的不同运行阶段进行CAS转换。

核心内部类Worker

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable
    {
        private static final long serialVersionUID = 6138294804551838833L;
        //worker对应的自己的线程
        final Thread thread;
         //该线程第一个任务
        Runnable firstTask;
        //运行的总任务数
        volatile long completedTasks;

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

        public void run() {
            runWorker(this);
        }

Worker是一个Runnable,同时拥有一个thread,这个thread就是要开启的线程,,在新建Worker对象时会同时新建一个Thread对象,同时将Worker自己作为参数传入TThread,这样当Thread的start()方法调用时,运行的实际上是Worker的run()方法,run()方法就会调用ThreadPoolExecutor类的runWorker方法。                                                                                                                  在线程池中,开启一共新线程,就是创建一个新的Worker,然后调用内部thread的start()方法                                           runWorker方法简化后如下:

final void runWorker(Worker w) {
		Runnable task = w.firstTask;
		w.firstTask = null;
		while (task != null || (task = getTask()) != null){
		task.run();
	}

 runWorker方法会一直调用getTask()方法来获取要执行的任务。getTask()方法简化如下:


private Runnable getTask() {
    if(一些特殊情况) {
        return null;
    }
 
    Runnable r = workQueue.take();
 
    return r;

getTask()就是从WorkQuene中获取还未执行的任务。因为BlockingQueue是个阻塞队列,BlockingQueue.take()得到如果是空,则进入等待状态直到BlockingQueue有新的对象被加入时唤醒阻塞的线程。所以一般情况Thread的run()方法就不会结束,而是不断执行从workQueue里的Runnable任务。

execute执行流程:

public void execute(Runnable command) {
        if (command == null) // 命令为null,抛出异常
            throw new NullPointerException();
        // 获取线程池控制状态
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { // worker数量小于corePoolSize
            if (addWorker(command, true)) // 添加worker
                // 成功则返回
                return;
            // 不成功则再次获取线程池控制状态
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) { // 线程池处于RUNNING状态,将命令(用户自定义的Runnable对象)添加进workQueue队列
            // 再次检查,获取线程池控制状态
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command)) // 线程池不处于RUNNING状态,将命令从workQueue队列中移除
                // 拒绝执行命令
                reject(command);
            else if (workerCountOf(recheck) == 0) // worker数量等于0
                // 这里百度是添加idle线程,对这部分还不理解
                addWorker(null, false);
        }
        else if (!addWorker(command, false)) // 添加worker失败
            // 拒绝执行命令
            reject(command);
    }

addWorker方法简化如下:

private boolean addWorker(Runnable firstTask, boolean core) {
 
    int wc = workerCountOf(c);
    if (wc >= (core ? corePoolSize : maximumPoolSize)) {
        return false;
    }
 
    w = new Worker(firstTask);
    final Thread t = w.thread;
    t.start();
}

所以线程池添加任务过程可以用下图表示:                                                                                                                                           submit()方法底层还是调用的execute方法。如果添加失败,会启动拒绝策略处理任务。

参考资料:https://blog.csdn.net/yjw123456/article/details/77719061

                  https://blog.csdn.net/he90227/article/details/52576452

                  https://www.cnblogs.com/leesf456/p/5585627.html

猜你喜欢

转载自blog.csdn.net/qq_42283110/article/details/86663447
今日推荐