目录

简介

自定义线程池的补充

线程池原理

 submit方法和execute方法的区别

addWorker方法

run方法

 runWorker方法后processWorkerExit方法

 异常处理

各位早上好下午好晚上好,吃了么您

ok,现在让我们来研究一下java线程池底层原理及对应的部分源码

简介

首先,什么是线程池,线程池有哪些,介个玩意咱就不过多仔细介绍了

本文主要用来补充线程池底层原理,并分析部分源码的

首先,我们已经知道我们常用的线程池就这四种

FixedThreadPool

CachedThreadPool

SingleThreadExecutor

ScheduledThreadPool

其中,前三种的底层创建方式,也就是构造方法,都是使用的

ThreadPoolExecutor

都是使用这个实现的,只不过对应的参数不同而已

自定义线程池的补充

关于这个自定义线程池如何使用

java 线程池上下文 springsecurity java线程池底层实现_工作线程

1、核心线程数并非可以一直存活,当 allowCoreThreadTimeout 设置为 true 的时候,核心线程也会被超时回收

2、线程池最大大小,指的是核心线程数+临时线程数

3、最大空闲时间和单位,都是针对临时线程的,而不是针对核心线程的

4、当线程数不够用的时候,任务会被放进阻塞队列

5、当任务队列满了,线程池也达到了最大数量,就会执行拒绝策略,默认策略为丢弃任务并抛出异常

补充一下其他线程池的信息

  • newCachedThreadPool中,传入的核心线程数为0,线程池最大为MAX,队列为synchronousQueue,说明线程都是临时线程,且由于队列的原因,这是一个生产消费队列,可以理解为队列容量为1,如果队列有任务,下个任务就无法存入,新任务先进队列,然后才会从队列被取走执行
  • newFixThreadPool,要求传入核心线程数大小和线程池的最大数量,意味着无临时线程,使用阻塞队列LinkedBlockingQueue,新任务直接执行,执行不了放队列
  • newSingleThreadExecutor,核心线程数为1,线程池最大为1,使用阻塞队列LinkedBlockingQueue

注意,这儿有个地方要注意,可缓冲线程池,也就是 newCacheThreadPool,对线程池最大数量未做限制,所以容易出现线程过多,CPU拉满的情况,谨慎使用

线程池原理

原理,其实就是一张图

java 线程池上下文 springsecurity java线程池底层实现_java_02

 submit方法和execute方法的区别

我们在线程池提交任务的时候,有两种方法可以提交上去,一个是submit,一个是execute,那这两个有什么区别呢?

我们直接去看

java 线程池上下文 springsecurity java线程池底层实现_工作线程_03

java 线程池上下文 springsecurity java线程池底层实现_工作线程_04

可以看到,submit有返回值,返回了Future,而execute没有返回值

并且,我们点进去submit方法

java 线程池上下文 springsecurity java线程池底层实现_java_05

可以看到,submit方法内部调用了execute方法,将任务传了下去

我们来看看 execute 方法执行逻辑,注意看好类,是 ThreadPoolExecutor 类

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
 
        int c = ctl.get();
        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);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

这就是execute方法的执行逻辑,可以看到,是有一些判断的,我们就一步一步来

上来第一个判断,可以很清晰的看到,它判断我们要执行的任务是不是为null,如果是null,直接抛空指针

下面的看起来就有点麻烦了

从名字上我们就可以看到,workerCountOf(c) 在统计工作线程数量,corePoolSize是核心线程数量

所以,这一段逻辑,我们可以化为这样

if(工作线程数量 < 核心线程数量) {

    创建核心线程

}

if(线程在运行状态 && 尝试把任务提交到队列) {

    if(!线程不是运行状态 && 尝试从队列移除任务) {

        拒绝策略

    } else if (工作线程数==0) {

            把任务交给非核心线程

        }

} else if (!能否把任务分给非核心线程) {

    拒绝策略    

}

这样是不是很明确就能看明白这一段逻辑了?

当然,我们可以看到,有一个很重要的方法,addWorker方法,这个其实就是创建工作线程,我们下面解释

注意一个地方

else if (workerCountOf(recheck) == 0)
                addWorker(null, false);

如果工作线程数为0,那就创建一个 null 任务,交给非核心线程运行,这有什么意义呢?

如果创建一个null任务,任务在队列存在了,就会被取出,保证线程池在 running 状态的时候,有一个任务在执行

addWorker方法

在上面,有好几个地方都执行了这个方法,说明这个方法很重要

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);
                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
            }
        }

        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;
    }

我们慢慢来看这个方法,一眼看过去,代码分为两部分,一部分为自旋,或者直接叫它死循环,一部分为其他处理逻辑,我们一部分一部分来看

首先,我们看自旋代码,自旋代码分为两部分,一部分是判断,另一部分是另一个自旋,我们先看判断

if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

我们可以看到,这是在判断线程的状态

java 线程池上下文 springsecurity java线程池底层实现_工作线程_06

采用了高低位来判断,就是判断是不是 SHUTDOWN 或以后的状态,并且任务为空,队列不为空,这个情况下,就不再增加 addWorker

然后我们继续看这个小自旋

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
            }

我们看到,第一行调用了一个方法,从方法的名字我们可以知道,它是在统计数量,统计什么数量呢?单单从这儿看,也不好说

我们看下面,有一个判断,wc是不是大于括号内计算出来的数量,括号内很明显,就是一个三目运算,这个core,是addWorker方法传进来的,也就是说,如果是核心线程,要判断wc是否大于等于核心线程数,如果是非核心线程,要判断是否大于等于线程池最大线程数

这儿其实就是判断是否会超出限制,如果超出了,那就直接返回一个false,否则就继续

然后我们看到,下一个判断调用了一个方法 compareAndIncrementWorkerCount(c)

我们看看这个方法是什么

private boolean compareAndIncrementWorkerCount(int expect) {
        return ctl.compareAndSet(expect, expect + 1);
    }

我们可以看到,它其实就是用CAS去对数量加了一个1,增加成功后,退出这个自旋

OK,到这里,addWorker的第一部分,也就是大的这部分自旋已经结束了,剩下的就是后半部分的逻辑,让我们来看看它做了什么(为了防止晕,我把后半部分代码再单独贴出来)

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;

可以看到,try里面,第一行,用传入的任务创建了一个worker,这个Worker就是真正要干活的

浅浅看一下这个类是什么样的

private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable

它继承了AQS,实现了Runnable,AQS先不管,实现Runnalbe,那就说明得实现run方法,而run方法的运行时机,就是.start()的时候,这个我们先记住

再看一下这个类的属性和构造方法

private static final long serialVersionUID = 6138294804551838833L;

        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;

        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }

三个属性,分别是线程、任务、完成任务的数量

我们暂时称呼new 出来的这个worker为工作者

首先,获取了工作者的线程,看为不为空,不为空的话,执行一大段逻辑,我们先看简单的,如果为空怎么办

如果为空的话,那就没有任何可以执行的,也就是说,finally里面的代码将会被执行,然后返回false,addWorker失败,因为默认 workerStarted 为false

从名字我们知道,finally的这个 if,就是用来执行添加worker失败的情况的,走,看看这个方法

private void addWorkerFailed(Worker w) {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            if (w != null)
                workers.remove(w);
            decrementWorkerCount();
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
    }

一目了然,加锁,从workers移除创建的worker,然后尝试减少worker的数量,记不记得,刚开始大自旋的时候,用CAS加了1,然后尝试结束,最后释放锁

我们回来,看这个 if 里执行了什么逻辑,当创建的worker的线程不为空的时候

可能有多个并发执行这个任务,所以先上锁,开始执行

但,在执行过程中也是需要判断状态的,有可能程序会出现中断或者其他情况

这儿还有一个判断,如果线程还没有启动,但线程处于存活状态,那说明现在是有问题的,这种情况,会直接抛异常

当把这种情况排除掉了,那它就把有效的这种worker,添加到workers中,然后判断一下workers的数量没有有大于设置的最大线程数

将workerAdded设置为true,释放锁

如果已经添加了,那么就调用worker的.start() 方法,并将workerStarted 标记为true,记不记得我们上面说的,什么时候Runnable的run方法会启动,那就是调用.start() 方法的时候

这就是这块的源码,当然,从代码上我们也可以看出优先级问题

假设提交一个任务

优先级1:核心线程池满了没,没满,创建核心线程执行,满了,执行优先级2

优先级2:队列满了没,没满,任务放在队列里,满了,执行优先级3

优先级3:线程池满了没,没满,创建非核心线程执行任务,满了,执行拒绝策略

run方法

我们已经知道,.start() 方法,会执行 run 方法,那么,run方法怎么执行的呢?

我们直接看线程池的run方法

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

哦?runWorker?

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

可以看到,第四行,对 runWorker进行了解锁,这个地方,可能乍一看有点懵,啥意思?

仔细看,后边的注释,允许中断,再过去,看一下 Worker 的构造方法

java 线程池上下文 springsecurity java线程池底层实现_java_07

看这句话,这句注释,在 runWorker 之前,不允许中断

state是什么?

java 线程池上下文 springsecurity java线程池底层实现_工作线程_08

其实 state 就是AQS内部维护的属性,表示资源可用状态,这个等之后AQS的时候就知道了

我们看 while 里面的条件,当task不为null的时候,才会从队列中去取任务,这一点也印证了队列的优先级确实是低,执行优先级上,左边大于右边

这一点,也可以理解为节省资源,毕竟,一个工作者手上有任务不做,再去从队列拿,不大好不大好~

然后在加锁之后,有一段判断,是什么意思呢?

if ((runStateAtLeast(ctl.get(), STOP) ||
                     (Thread.interrupted() &&
                      runStateAtLeast(ctl.get(), STOP))) &&
                    !wt.isInterrupted())
                    wt.interrupt();

这一段意思,其实就是在判断线程池的状态,是不是 STOP,如果是 STOP,那就中断,真正执行逻辑的,就是下面的try cache 代码块

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);
                    }

比较显眼的 就是 beforeExecute 和 afterExecute,如果点进去看,会发现没有实现,这意味着我们可以根据实际需要去做这个实现

去除这两个代码,其实核心代码就是 task.run()

看好,在这个 finally 执行 afterExecute 方法之后,后边还有一个 finally

finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }

这个就很简单了,任务置为null,worker的完成数量加一,解锁

加油,就快完了!

回顾一下,一开始的 try,还有一个最后最大最外层的 finally

java 线程池上下文 springsecurity java线程池底层实现_线程池_09

 runWorker方法后processWorkerExit方法

这是最后执行的退出方法,也是非常重要的

private void processWorkerExit(Worker w, boolean completedAbruptly) {
        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)) {
            if (!completedAbruptly) {
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }

 前面的我们也不用关注很多,我们看最后,满足条件的话,它会调用 addWorker,创建了一个null任务,非核心线程

综合来说,其流程是这样的

java 线程池上下文 springsecurity java线程池底层实现_源码分析_10

 异常处理

通过submit提交的线程,可以屏蔽线程中产生的异常,可以保证线程的复用,当使用get的时候,才会抛出对应的异常

所谓屏蔽异常,其实是将异常保存了,future.get的时候,就会返回对应的异常

可以看一下 FutureTask的run方法

java 线程池上下文 springsecurity java线程池底层实现_工作线程_11

 

java 线程池上下文 springsecurity java线程池底层实现_线程池_12

java 线程池上下文 springsecurity java线程池底层实现_线程池_13

看这个 outcome 的注释

我们再来看看get方法

public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

可以看到,最后有一个 report 方法

private V report(int s) throws ExecutionException {
        Object x = outcome;
        if (s == NORMAL)
            return (V)x;
        if (s >= CANCELLED)
            throw new CancellationException();
        throw new ExecutionException((Throwable)x);
    }

可以看到,outcome是异常时才抛出

至于,为什么让大家看 FutureTask 类呢?因为就是基于它在运行,不信?咱瞧瞧

java 线程池上下文 springsecurity java线程池底层实现_工作线程_14

咱们看看这个方法是什么,返回了一个 RunnalbeFuture

protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new FutureTask<T>(runnable, value);
    }

看,创建了一个 FutureTask,至于它本什么

java 线程池上下文 springsecurity java线程池底层实现_工作线程_15