过年放假在家里也没事情做,就继续整理整理平时看的东西总结一下写一篇博客。这个博客我自己也会看看所以有时候会讲的特别的详细(啰嗦),因为我也怕忘记了。如果对我的博客或者对我有什么建议或者意见大家可以提出来,我会认真对待的,大家一起学习一起提升;
这次分析的是Java的线程池,可能之前已经有很多大佬分析过了,好了那我就不写了,开玩笑啦。人家分析的是人家的见解,我总结的是我自己的想法,自己总结一遍印象肯定也深刻一些啊。好了废话不多说。
有些人总以为这个线程池很厉害非常流弊。那这个线程池到底能干嘛呢?能管线程的生命周期,生命周期欸(瞬间高大上)!还有各种策略,阻塞队列,线程复用。这简直就刁的不行啊!这个里面的套路很深啊!其实里面真的没有你们想的那么复杂,它的实现非常清晰,稍微分析看看就能清楚的知道它是怎么工作的了。
首先我们就这个线程池来提几个问题吧:
- 线程池构造函数的参数都有什么意义?任务什么时候直接执行什么时候缓存到队列?
- 线程池是怎么分配线程处理传入的Runnable(Callable)的?
- 线程池阻塞队列缓存的策略又是怎么样的?
- 执行任务(Runnable)的线程是从哪里来的?
- 线程池是怎么让线程复用?
- 线程池怎么管理线程的生命周期?
带着上面几个问题去看源码吧,源码里面全部都有。我会非常详细的来分析这个过程,告诉你他的套路是怎么样的。
好了,首先我们可以把线程池当成一个老板一个公司,他管理着非常多的员工(Worker也就是线程);员工不够了可以去招聘(创建线程)一些员工,员工多了就得炒掉(结束线程生命周期)一些员工;
然后来一个简单的线程池用法的例子吧
创建
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(128), sThreadFactory);
[2]执行
threadPoolExecutor.execute(mRunnable);//可以用submit(),不过里面其实还是调用的execute()
我偷懒把之前分析的AsyncTask里面的线程池拿出来贴上去了,好了下面来分析一下吧。
第一步创建实例,首先看一下它的构造函数这个上来就是乱七八糟一坨参数。莫慌,我们一个一个看
CORE_POOL_SIZE: 可以理解为核心线程的数量,核心线程一般情况下(不设置allowCoreThreadTimeOut(true)
)创建出来不会因为长时间不处理任务就被结束掉(可以理解为核心员工的,很厉害的员工,就算接不到业务了公司也得留着他们)
MAXIMUM_POOL_SIZE:线程池最大的线程数量(这个值可以理解为公司的规模,公司总共有多少雇员)
KEEP_ALIVE_SECONDS:一般情况下这个值是针对非核心线程的(设置allowCoreThreadTimeOut(true)
就针对所有线程了),这是一个超时时间,也就是一个线程超过这个时间没有处理过任务(可能以前处理过了,但是后来空闲下来了没有任务了)那这个线程的生命周期就会结束;(就好比一些员工(往往是临时工),公司业务不行平时没有啥事,然后这些员工就闲着了,那你既然都闲着超过了我规定的一段时间都没干过活,我养你还有何用,直接炒鱿鱼了)
TimeUnit.SECONDS:这个就是一个时间的单位,描述KEEP_ALIVE_SECONDS这个值的单位,没啥好说的;
BlockingQueue:LinkedBlockingQueue是BlockingQueue的一种,它就是存放任务(runnable)的一个容器(队列),当所有的核心线程都在处理任务了,这个时候队列没满多余的任务就会被扔进去,缓存起来等待一会儿有空闲的线程来处理;
ThreadFactory:简单的理解为一个创造线程的工厂类(人才市场,提供员工的)
好了这里参数就大致有个了解就行了,后面用到的时候就能很清楚的知道他们的特点意义了;
execute(Runnable runnable)方法
现在主要是来分析一下execute(mRunnable)
方法,这个方法只需要一个参数也就是一个我们需要线程池帮我们完成的任务(Runnable),这个方法也是线程池所有逻辑(套路)的入口;那好我们就拿起鼠标戳它,看看到底里面是啥东西;
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
//代表线程池正在运行的状态
private static final int RUNNING = -1 << COUNT_BITS;
//代表线程池被暂停了,但是还会把队列中缓存着的任务全部都搞定
private static final int SHUTDOWN = 0 << COUNT_BITS;
//代表线程池被停止了,不在接收新的任务,并且正在运行的线程也会被中断(interrupt = true)
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
[1]
int c = ctl.get();
[2]
//workerCountOf(c)就是通过各种移位计算从这个c(又带有线程池状态,又带有线程池线程数量)
//获得当前线程池线程数量的一个方法;
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
[3]
//isRunning(c)也是一个封装方法,其实就是移位计算从ctl中拿到状态看看是不是Running状态
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);
}
[4]
else if (!addWorker(command, false))
reject(command);
}
private boolean addWorker(Runnable firstTask, boolean core) {
...
}
乍一看里面出现最多的方法就是addWorker方法,出现那么多次那这个方法肯定很重要,我们从名字看就可以猜一下这个方法的作用是添加一个Worker来处理我们给他的任务(runnable)的,那这个worker是什么呢?既然是线程池里面的东西这个worker我们猜他就是一个线程咯;
接着看代码,方法一开始就是让我们不要把空的任务传给线程池,否则直接就一个异常甩你脸上!
[1]从这里开始先来看看c = ctl.get()
这个ctl是什么?上面给出了它的定义,是一个AtomicInteger,其实就是一个可以原子操作防止并发的Integer