上一期简单的聊了一下线程池的创建。这一期我们就,稍微熟悉一下线程池执行任务的过程吧!
线程池的工作原理是怎么样的呢?
首先,我用图来说明吧!
这里可以看到有一个任务队列,然后还有一个工作的线程池。今天主要将的就是向任务队列中添加任务的过程!
对于线程池我们上一期是做了一个稍微的解释的。我们构建线程池的时候,无论我们传入队列或者是不传入最后都会有一个队列的。那么这个队列是用来干什么的呢?当我们提交的任务不能被及时执行的时候,可以入队列,等待被执行的。现在对于线程池的提交任务流程做一个简单的图解。
这里我们主要以cachedThreadPool为例。
图中所有创建的线程都会被放到线程池的hashset中记录下来!
这个流程图大概分为以下几个部分:
- 提交任务首先判断是否是null,如果是null就抛出空指针异常,如果不是执行2。
- 判断线程池中工作的线程数是否小于核心线程池的大小。如果小于执行 3,如果大于执行4
- 创建一个线程来执行提交的任务,如果失败重新获取线程池状态,然后执行4,成功就返回
- 判断线程池的状态是否在运行和尝试向把任务加入等待队列,如果线程池已经关闭或者放入等待队列失败,执行6。如果放入队列成功执行5
- 因为成功的入队了,我们需要重新判断线程池的状态,因为在放入队列的过程中线程池的状态可能会发生变化。如果发现线程池的状态不是在运行就将刚才添加的任务从队列中移除,然后执行拒绝策略。如果是在运行状态执行7
- 尝试创建一个线程来执行任务,如果创建线程失败就执行拒绝策略
- 判断当前的工作线程数是不是0如果不是就返回,如果是就创建一个线程!
public void execute(Runnable command) {
//首先判断传入的线程是不是null,没什么好说的
if (command == null)
throw new NullPointerException();
//这里的ctl是什么呢? private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));这是一个原子操作类,
//线程池里面对这个原子操作类做出了,比较清晰的解释的。我大概在这里说一下吧。这个整数有一共32位,其中最高三位用来,存储线程池的状态。低29位用来存储线程池活跃的线程数目。
//线程池的状态一共有以下几种情况
//正在运行,能够接受新任务,以及对应添加的任务进行处理
//private static final int RUNNING = -1 << COUNT_BITS;
//关闭,不接受新的任务但是可以处理已经添加的任务
//private static final int SHUTDOWN = 0 << COUNT_BITS;
//停止,不接受新任务,但是也不会处理已经添加的任务。
//private static final int STOP = 1 << COUNT_BITS;
//整理,所有的任务都已经处理结束线程池的状态。这个时候线程池会执行一个函数terminated()现在模拟人的处理都是空的
//private static final int TIDYING = 2 << COUNT_BITS;
//线程池彻底的终止。整理状态调用terminated函数以后就会变成这个状态了
//private static final int TERMINATED = 3 << COUNT_BITS;
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();
//如果成功入队的话,我们需要重新的判断一下状态,因为从上一次check、到现在可能会有线程死亡,或者线程池关闭了。如果线程池关闭了我们就拒绝这个任务,如果有线程死亡我们就创建新的线程
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//创建线程失败了,就拒绝任务就好了!
else if (!addWorker(command, false))
reject(command);
}
大家可能发现了,执行任务的时候有一个submit还有一个execute这两个方法的区别就是:submit会返回一个异步的结果。但是execute方法没有返回值。这个很清楚吧!
个人认为单就提交任务的 流程算是比较清楚了!本期没有代码,因为分析的都是源码!想看代码的小伙伴们可以直接看jdk1.7的源代码!欢迎交流!
现在很多电影都有彩蛋,这个线程池系列也来了个彩蛋吧!下一期将会说一下创建新线程添加到hashset和线程池的关闭过程!敬请期待!!!