线程池ThreadPoolExecutor实现原理

线程是一个程序员一定会涉及到的一个概念,但是线程的创建和切换都是代价比较大的。所以,我们有没有一个好的方案能做到线程的复用呢?这就涉及到一个概念——线程池。合理的使用线程池能够带来3个很明显的好处:
1.降低资源消耗:通过重用已经创建的线程来降低线程创建和销毁的消耗
2.提高响应速度:任务到达时不需要等待线程创建就可以立即执行。
3.提高线程的可管理性:线程池可以统一管理、分配、调优和监控。

1. ThreadPoolExecutor实现原理

ThreadPoolExecutor的构造方法
public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
corePoolSize:线程池中的核心线程数;
maximumPoolSize:线程池最大线程数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:线程池中非核心线程闲置超时时长(没有任务执行时的回收时间);
    一个非核心线程,如果闲置状态的时长超过这个参数所设定的时长,就会被销毁掉
TimeUnit:时间单位。可选的单位有分钟(MINUTES),秒(SECONDS),毫秒(MILLISECONDS) 等;
workQueue:任务的阻塞队列,缓存将要执行的Runnable任务,由各线程轮询该任务队列获取任务执行。可以选择以下几个阻塞队列。
    ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
    LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
    SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
    PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
ThreadFactory:线程创建的工厂。可以进行一些属性设置,比如线程名,优先级等等,有默认实现。
RejectedExecutionHandler:任务拒绝策略,当运行线程数已达到maximumPoolSize,队列也已经装满时会调用该参数拒绝任务,默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
    AbortPolicy:直接抛出异常。
    CallerRunsPolicy:只用调用者所在线程来运行任务。
    DiscardOldestPolicy:丢弃队列里最老的一个任务,并执行当前任务。
    DiscardPolicy:不处理,丢弃掉。

在这里插入图片描述

ThreadPoolExecutor的状态变量
 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // runState is stored in the high-order bits
    //COUNT_BITS=29   左移29位
    //1 =  0000 0000 0000 0000 0000 0000 0000 0001
	//-1 = 1111 1111 1111 1111 1111 1111 1111 1111
	// RUNNING  = 111000 0000 0000 0000 0000 0000 0000 0000
    private static final int RUNNING    = -1 << COUNT_BITS;
	
	//SHUTDOWN   = 000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //stop   =  001
    private static final int STOP       =  1 << COUNT_BITS;
    //TIDYING  =010
    private static final int TIDYING    =  2 << COUNT_BITS;
    //TERMINATED =  011
    private static final int TERMINATED =  3 << COUNT_BITS;
其中ctl是ThreadPoolExecutor的同步状态变量。
workerCountOf()方法取得当前线程池的线程数量,算法是将ctl的值取低29位。
runStateOf()方法取得线程池的状态,算法是将ctl的值取高3位:
	RUNNING 111 表示正在运行
    SHUTDOWN 000 表示拒绝接收新的任务
	STOP 001 表示拒绝接收新的任务并且不再处理任务队列中剩余的任务,并且中断正在执行的任务。
	TIDYING 010 表示所有线程已停止,准备执行terminated()方法。
	TERMINATED 011 表示已执行完terminated()方法。
execute()方法
 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.
         */
         //  ctl保存的是线程池的状态信息
        int c = ctl.get();
        //1.首先获取当前线程池的工作线程数量,是否小于核心线程数量
        if (workerCountOf(c) < corePoolSize) {
        	//addWorker(command, true)创建核心线程。并将任务command添加到该线程中,然后启动线程
            if (addWorker(command, true))
                return;
            //再次获取线程池的状态信息
            c = ctl.get();
        }
        //2.判断线程池的状态,如果是Running状态,并且阻塞队列workQueue未满
        if (isRunning(c) && workQueue.offer(command)) {
        	//获取线程池的状态信息
            int recheck = ctl.get();
            //再次获取线程池的状态,如果不是running状态就从阻塞队列中移除任务
            if (! isRunning(recheck) && remove(command))
            	//执行拒绝策略
                reject(command);
            //如果线程池为空,创建线程并执行
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        //通过addWorke创建一个线程,并将任务添加到该线程,然后启动。如果addWorker执行失败,执行拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

以上代码对应了三种情况:

  • 线程池的线程数量小于corePoolSize核心线程数量,开启核心线程执行任务。
  • 线程池的线程数量不小于corePoolSize核心线程数量,或者开启核心线程失败,尝试将任务以非阻塞的方式添加到任务队列。
  • 任务队列已满导致添加任务失败,开启新的非核心线程执行任务。

在这里插入图片描述

addWork()方法

线程池是维护了一批线程来处理用户提交的任务,达到线程复用的目的,线程池维护的这批线程被封装成了Worker。

private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        //使用CAS机制轮询线程池的状态,如果线程池处于SHUTDOWN及大于它的状态则拒绝执行任务
        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
            }
        }

		//这里已经成功执行了CAS操作将线程池数量+1,下面创建线程
        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;
    }

addWorker这个方法先尝试在线程池运行状态为RUNNING并且线程数量未达上限的情况下通过CAS操作将线程池数量+1,接着在ReentrantLock同步锁的同步保证下判断线程池为运行状态,然后把Worker添加到HashSet workers中。如果添加成功则执行Worker的内部线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值