java线程池ThreadPoolExexutor源码解析

ThreadPoolExecutor executor = new ThreadPoolExecutor(0, 1, 1, TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());
            try {

                executor.execute(new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println(1);
                    }
                }));
            }catch (Exception e){
                e.printStackTrace();
            }

源码入口executor.execute,点进去看看里面执行了什么

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
 //获取线程池的信息,ctl是一个32位的数字,高3位保存了线程池状态,低29位保存线程数量
        int c = ctl.get();
  //如果当前工作线程数少于核心线程,走下面这个逻辑,addWorker
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
  //如果线程池正常运行,且任务入队成功
        if (isRunning(c) && workQueue.offer(command)) {
          //重新检查线程池状态
            int recheck = ctl.get();
          //如果线程池非运行状态,丢弃刚放的任务,进入拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
          //如果工作线程数=0,就创建空任务,让新的队列去消费
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
  //线程池非运行状态,或者任务入队失败,才会走到这个逻辑
  //如果线程池非运行状态,是不能丢任务进去的,这个addWorker方法直接返回false,进入拒绝策略
  //如果只是队列满,再尝试添加一次任务,如果添加失败,进入拒绝策略
        else if (!addWorker(command, false))
            reject(command);
    }

addWorker()

先来看看addWorker做了什么

/**
* firsttask 提交的任务
* core 是否提交给核心线程工作
*/
private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();//获取线程池信息
            int rs = runStateOf(c);//或者线程池运行状态信息
          //运行状态在正常情况下一般都是高3位不动的,也就是负数,负很大

            // 当线程状态大于等于SHUTDOWN(0)且(线程状态等于SHUTDOWN,提交的任务为null,任务队列不为空)这3个条件有一个不符合,都会走到false逻辑,也就是线程任务提交失败
          //实际上,线程状态大于等于SHUTDOWN,也就是线程池进入了SHUTDOWN状态,一般情况下,只有通过shutdown()或者shutdownnow()方法才能进入
          //shutdown()停止线程池,无法提交新任务了,但是旧存在的任务还是要执行完
          //shutdownnow()也是停止线程池,但是池内原有任务直接被丢弃
          //但是不管shutdown()还是shutdownnow(),新的任务都是无法添加,所以走到这都会return false
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
              	//获取线程池内工作线程数量
                int wc = workerCountOf(c);
              //如果任务提交给核心线程,判断当前核心线程满了么,满了return false;否则判断当前工作线程数是否超了最大线程数
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
              //cas去增加1工作线程数,如果自增成功,可以退出这个自旋逻辑,开始用这个线程执行任务啦
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // 为什么这里要再取一遍线程池状态?因为上一步CAS失败,说明其他线程优先执行了操作,对线程池的状态或者工作线程数量可能修改了,所以需要重取一遍
              //如果线程状态变化,要重新走一遍最外层的逻辑
                if (runStateOf(c) != rs)
                    continue retry;
              //若无变化,则只是CAS竞争失败而已,重新进行自旋
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

        boolean workerStarted = false;//标记工作线程是否开始执行
        boolean workerAdded = false;
        Worker w = null;//Worker是什么?可以理解为消费者。线程池内维护了一个任务队列workQueue保存线程任务,workers是个hashset,保存了这些worker,也就是核心线程,最大线程的线程。
        try {
          //初始化一个新的工作线程,先往后看,看看Worker类里面放了什么
            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类

很奇怪,为什么要把线程封装到worker中?线程池直接丢线程消费或者让线程去workQueue消费不就好了?实际上,使用worker类是为了更好的管理线程的中断

// 此处可以看出 worker 既是一个 Runnable 任务,也实现了 AQS(实际上是用 AQS 实现了一个独占锁,这样由于 worker 运行时会上锁,执行 shutdown,setCorePoolSize(),setMaximumPoolSize等方法时会试着中断线程(interruptIdleWorkers) ,在这个方法中断方法中会先尝试获取 worker 的锁,如果不成功,说明 worker 在运行中,此时会先让 worker 执行完任务再关闭 worker 的线程,实现优雅关闭线程的目的)
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
    {
        private static final long serialVersionUID = 6138294804551838833L;

        // 实际执行任务的线程
        final Thread thread;
        // 上文提到,如果当前线程数少于核心线程数,创建线程并将提交的任务交给 worker 处理处理,此时 firstTask 即为此提交的任务,如果 worker 从 workQueue 中获取任务,则 firstTask 为空
        Runnable firstTask;
        // 统计完成的任务数
        volatile long completedTasks;

        Worker(Runnable firstTask) {
            // 初始化为 -1,这样在线程运行前(调用runWorker)禁止中断,在 interruptIfStarted() 方法中会判断 getState()>=0
            setState(-1); 
            this.firstTask = firstTask;

            // 根据线程池的 threadFactory 创建一个线程,将 worker 本身传给线程(因为 worker 实现了 Runnable 接口)
            this.thread = getThreadFactory().newThread(this);
        }

        public void run() {
            // thread 启动后会调用此方法
            runWorker(this);
        }

        // 1 代表被锁住了,0 代表未锁
        protected boolean isHeldExclusively() {
            return getState() != 0;
        }

        // 尝试获取锁
        protected boolean tryAcquire(int unused) {
            // 从这里可以看出它是一个独占锁,因为当获取锁后,cas 设置 state 不可能成功,这里我们也能明白上文中将 state 设置为 -1 的作用,这种情况下永远不可能获取得锁,而 worker 要被中断首先必须获取锁
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        // 尝试释放锁
        protected boolean tryRelease(int unused) {
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }    

        public void lock()        { acquire(1); }
        public boolean tryLock()  { return tryAcquire(1); }
        public void unlock()      { release(1); }
        public boolean isLocked() { return isHeldExclusively(); }

        // 中断线程,这个方法会被 shutdowNow 调用,从中可以看出 shutdownNow 要中断线程不需要获取锁,也就是说如果线程正在运行,照样会给你中断掉,所以一般来说我们不用 shutdowNow 来中断线程,太粗暴了,中断时线程很可能在执行任务,影响任务执行
        void interruptIfStarted() {
            Thread t;
            // 中断也是有条件的,必须是 state >= 0 且 t != null 且线程未被中断
            // 如果 state == -1 ,不执行中断,再次明白了为啥上文中 setState(-1) 的意义
            if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
                try {
                    t.interrupt();
                } catch (SecurityException ignore) {
                }
            }
        }
    }

runWorker()

final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
        w.firstTask = null;
  //unlock 会调用 tryRelease 方法将 state 设置成 0,代表允许中断
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
          //循环消费任务队列内任务,getTask()也是线程退出的一个核心方法
            while (task != null || (task = getTask()) != null) {
                w.lock();
               //判断线程是否进入中断逻辑
                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++;//当前worker已消费的任务数+1
                    w.unlock();
                }
            }
            completedAbruptly = false;
        } finally {
          //如果执行到这只有两种可能,一种是执行过程中异常中断了,一种是队列里没有任务了,从这里可以看出线程没有核心线程与非核心线程之分,哪个任务异常了或者正常退出了都会执行此方法,此方法会根据情况将线程数-1
            processWorkerExit(w, completedAbruptly);
        }
    }

processWorkerExit()处理消费线程退出

private void processWorkerExit(Worker w, boolean completedAbruptly) {
  //如果线程异常退出,cas线程数-1
    if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
        decrementWorkerCount();

    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;
      //加锁线程安全地移除工作线程
        workers.remove(w);
    } finally {
        mainLock.unlock();
    }
	//工作线程退出,可能线程池状态变了,尝试关闭线程池
    tryTerminate();
    //重新获取线程池状态
    int c = ctl.get();
  	//如果线程状态没到停止态
    if (runStateLessThan(c, STOP)) {
      //如果线程正常流程走下来,这个completedAbruptly是false,也就走进这个if逻辑
        if (!completedAbruptly) {
          //最小线程数,如果允许核心线程超时,就是0,不然就是核心线程数
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && ! workQueue.isEmpty())
                min = 1; //如果还有任务没消费完,那这个最小线程数可不能为0,至少为1
          //如果当前的工作线程数大于最小线程数,说明有足够的线程在消费任务,就当无事发生
            if (workerCountOf(c) >= min)
                return; // replacement not needed
        }
      //加入空任务,新建线程去消费
        addWorker(null, false);
    }
}

到此为止,考虑的情况都是池内线程数小于核心线程数。接下来考虑池内线程数大于核心线程数,也就是

int wc = workerCountOf(c);
if (wc >= CAPACITY ||
    wc >= (core ? corePoolSize : maximumPoolSize))
    return false;

addWorker方法返回了false。现在核心线程的任务丢不进去了,回到execute方法的第3个if

if(isRunning© && workQueue.offer(command))

显然正常状态下,任务队列不满,这个逻辑就是直接把任务丢进队列,结束~,队列里的任务有核心线程在消费咯(getTask()方法)。

核心线程or非核心线程是怎么下线的?

private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    for (;;) {
      //获取线程池状态
        int c = ctl.get();
      //获取线程池运行信息
        int rs = runStateOf(c);

        // 如果线程池SHUTDOWN了,且已经停或者队列空,减1工作线程数,结束当前gettask
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // 若满足允许核心线程超时或工作线程数大于核心线程数,则timed=true
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
		//如果线程数大于最大线程数或者都超时且
        if ((wc > maximumPoolSize || (timed && timedOut))
            //表示工作线程不止这个或者队列为空,那没有工作线程也没关系
            && (wc > 1 || workQueue.isEmpty())) {
          //减少工作线程
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
          //从任务线程里取任务,如果timed,则进入回收策略,在超时时间内都没有取到任务的话,证明不需要这个线程啦,下次循环,工作线程数-1
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

至于这个worker,在后续的processWorkerExit()方法内被移除

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值