简述线程池ThreadPoolExecutor设计思想

线程池简述ThreadPoolExecutor 设计思想

ThreadPoolExecutor 应用场景

通过线程池的使用减轻小反复创建线程带了的开销,JDK提供的线程池主要有5中,前3种线程池的构造都是通过调用ThreadPoolExecutor的重载构造方式创建,只是在传入的参数上有不同的设置。
1. newFixedThreadPool 固定数量线程池
2. newCachedThreadPool 根据实际情况调整线程数的线程池
3. newSingleThreadExecutor 只有一个线程的线程池
4. newScheduledThreadPool 返回一个定时执行任务的线程池
5. newSingleThreadScheduledExecutor 一个线程数为1的定时执行线程池
对线程池了解之前了解ThreadPoolExecutor的非常重要

需要掌握ThreadPoolExecutor中的构造参数

  1. corePoolSize 核心线程数:在提交线程任务小于核心线程数的情况下,会通过创建性的线程来执行任务,默认情况下:线程池中线程的数量会稳定在核心线程数指定的数量。
  2. maximumPoolSize 最大线程数:线程池中的线程数量一定是在最大线程数以下。
  3. keepAliveTime 空闲线程最大存活时间:出现空闲线程的情况时,当线程数量大于corePoolSize的时候,空闲的线程在等待keepAliveTime时间后会停止运行直到线程数量降至corePoolSize。
  4. unit 存活时间单位:作为keepAliveTime 的修饰
  5. workQueue 任务缓冲队列:当前线程数大于等于corePoolSize 的时候,新加入的任务会被放到workQueue中,进行缓存。如果workQueue装满了,就需要执行拒绝策略。
  6. threadFactory 线程工厂:通过工厂构建的渠道创建线程,默认的效果是执行new Thread()
  7. handler 拒绝策略:在任务队列装满,同时当前线程数量等于maximumPoolSize,即无法通过扩展线程数来保证任务的执行的情况下,执行相应的拒绝策略。

重要的内部类Worker

Worker实现RunnableWork接口,在ThreadPoolExecutor中代表了着一个执行的线程,所有的Worker放在ThreadPoolExecutor 中的workers集合属性中。Woker的数量代表的就是线程的数量。
Worker的主要任务就是从workQueue 不断的通过getTask() 取任务,在workQueue中有任务的情况下则立即返回队列中的任务task,取出任务task后执行task的run() 方法,否者就会阻塞在getTask() 方法上面(这个地方类似消费者模式)。getTask() 在线程数量超过corePoolSize的情况下是定时等待获取,等待时间为keepAliveTime ,当超过keepAliveTime 会停止等待返回null任务,导致Worker任务跳出循环任务,跳出循环后会进行执行Woker的退出逻辑;在没有超过corePoolSize的情况下执行的是阻塞等待的操作,等待workQueue 放入新的任务然后返回task。

while (task != null || (task = getTask()) != null) {//不断从workQueue中获取任务
//...状态判断逻辑
 beforeExecute(wt, task);//自定义任务执行前逻辑
 task.run();//将获取的任务执行
 afterExecute(task, thrown);//自定义任务执行后逻辑
 }
 //...
 finally {
    processWorkerExit(w, completedAbruptly);//执行当前worker线程离开前的任务
}

在创建线程池的时候可以通过重写ThreadPoolExecutor的beforeExecute 方法以及afterExecute 方法来实现任务执行前后的逻辑定义。

任务提交方法execute(Runnable command)

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
            //当前线程的数量以及当前线程池的状态,都被包装在ctl中。ctl的高位表示状态,低位表示当前线程池的数量
        int c = ctl.get();
        //当前线程数量小于核心线程数量则创建一个Worker线程执行当先任务
        //addWorker(command, true),true表示在核心线程数范围内添加新线程
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //当前线程池RUNNING的情况下,当前线程数量大于核心线程数量,队列未满,向workQueue队列中排队当前任务
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //检查在任务加入队列期间,线程池是否仍然处于运行状态。
            //如果线程池已经进入停止运行的状态,拒接才提交的command任务
            if (! isRunning(recheck) && remove(command))
                reject(command);//执行任务拒绝策略
             //如果线程池仍然在运行状态,但是线程池中没有Woker的情况(Woker都因为超时退出了),为保证提交到队列中的command任务被执行,所以创建一个firstTask为null的Worker线程去处理线程池中的任务。
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //当前线程数量大于核心线程数量,由于队列已满,所以尝试创建新的线程,如果添加新的线程导致超出maximumPoolSize,则执行拒绝策略。
        else if (!addWorker(command, false))
            reject(command);
    }

用户调用execute方法向线程池提交一个任务,提交任务时根据当前的ctl 获取当前线程池中线程数量判断是否要创建新的Worker线程来服务,还是将任务添加到任务队列中,或者是通过拒绝策略拒绝此任务,执行过程见代码注释。

Worker线程添加

    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);
                /**
                * 1.保证线程数量不超过最大线程池数量接近Integer
                * 2.在通过传入的限制条件,限制是否继续执行添加
                */
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                // 如果当前的运行状态不等于rs,说明状态已被改变,返回第一个for循环继续执行
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }
/ 第二步 创建一个Worker,包装当前的任务,并启动该work中创建的线程,用于执行当前当前提交过来的任务
        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);//新建一个worker同时从ThreadFactory中创建一个新的线程
            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);//放入worker集合
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {//worker添加成功,启动任务
                    t.start();
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

首先使用CAS操作保证成功增加workerCount,然后将创建一个worker,将worker添加到workers池,启动worker,返回任务添加成功,启动运行创建好的Worker线程,致run() 方法运行便Woker开始执行firstTask,执行完firstTask任务后从WorkerQueue中取任务执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值