Android线程池

上篇将android多线程篇幅过长,所以决定线程池这一张拆开来讲。其实在我的okhttp源码理解内已经谈过线程池的使用,这边我们稍微详细谈谈线程池。一般我们使用线程池都会用Executors去获得相应的线程池对象,而线程池总共有4种

  • newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程,亦即无线扩增线程池。在线程空闲60秒后终止线程。
public static ExecutorService newCachedThreadPool() {    
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}```

* newFixedThreadPool  创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);
}```

  • newScheduledThreadPool 继承ThreadPoolExecutor,创建一个定长线程池,支持定时及周期性任务执行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {    
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}```
* newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),threadFactory));
}```

显然,上面4种线程池都直接或者间接的来自于ThreadPoolExecutor,Executors只是提供了一个抽象工厂的功能去申请一个用户需要的线程池,那么我们就来看看ThreadPoolExecutor到底是什么。

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,                        
                  long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,  
                        ThreadFactory threadFactory, RejectedExecutionHandler handler) {   
 if (corePoolSize < 0 ||maximumPoolSize <= 0 ||maximumPoolSize < corePoolSize ||keepAliveTime < 0)        
                        throw new IllegalArgumentException();    
if (workQueue == null || threadFactory == null || handler == null)       
                         throw new NullPointerException();    
                        this.corePoolSize = corePoolSize;    
                        this.maximumPoolSize = maximumPoolSize;   
                        this.workQueue = workQueue;    
                        this.keepAliveTime = unit.toNanos(keepAliveTime);   
                        this.threadFactory = threadFactory;    
                        this.handler = handler;
}```
* corePoolSize 核心线程数
* maximumPoolSize 线程池最大线程数,它表示在线程池中最多能创建多少个线程;
* keepAliveTime 表示线程没有任务执行时最多保持多久时间会终止。(默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;)
* workQueue 一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有几种选择,常用的有1. LinkedBlockingQueue(此队列按 FIFO(先进先出)排序元素) 2. SynchronousQueue(一个缓存值为1的阻塞队列)有兴趣的可以了解其他的blockQueue
* threadFactory 一个接口用来生成一个Thread
* handler: RejectedExecutionHandler类型表示当拒绝处理任务时的策略,有以下四种取值:
![handler策略.png](http://upload-images.jianshu.io/upload_images/1868403-724785fed399d79f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

然后看看它的父类AbstractExecutorService,这是一个抽象类
![AbstractExecutorService结构图.png](http://upload-images.jianshu.io/upload_images/1868403-6b615cad82eb9d04.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
它实现了ExecutorService接口,对应贴出此接口的结构图
![ExecutorService结构图.png](http://upload-images.jianshu.io/upload_images/1868403-642f012f3cd89b1f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
ExecutorService也是继承了Executor,Executor只有一个void execute(Runnable command)方法 就不贴了
清晰了ThreadPoolExecutor的继承关系后 我们回过头来看看ThreadPoolExecutor
######在此之前我们看看线程的状态

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int RUNNING = -1 <<COUNT_BITS;
private static final int SHUTDOWN = 0 <<COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 <<COUNT_BITS;

ctl是一个线程安全的自增,自减数据结构,记录
* RUNNING :Accept new tasks and process queued tasks线程池正常运行状态
* SHUTDOWN :Don't accept new tasks, but process queued tasks调用shutDown(),进入此状态,不接受新的任务,等待正在执行的任务完成
* STOP :Don't accept new tasks, don't process queued tasks,and interrupt in-progress tasks调用shutdownNow(),不接受新任务,并主动尝试去结束正在执行的任务
* TIDYING :All tasks have terminated, workerCount is zero,the thread transitioning to state TIDYING will run the terminated() hook method 线程池为空,就会到达这个状态,执行terminated()方法
* TERMINATED terminated()执行完毕,所有任务结束后即进入此状态

#####接着我们看看几个主要的方法
* void execute(Runnable command) 执行任务

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流程.png](http://upload-images.jianshu.io/upload_images/1868403-66348c50eb4ff5c3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
有点乱,呵呵呵,大概讲解下。首先,判断当前工作队列是否有空闲,尝试加入工作队列,如果成功则结束,否则继续下面判断,判断线程池是否为running状态,并将任务加入到阻塞队列,如果这两个条件有一个不符合,则扩容线程池不大于最大限制情况下尝试加入,未成功的话,抛出拒绝句柄,若两个条件符合,重新检测线程池状态,如果线程池不再running状态则尝试移除该任务,并抛出拒绝句柄,否则判断当前工作线程数是否为0,暂停任务(这边addWorker(null,false)不太理解,请高手指出)
* submit() 执行任务并返回结果,在AbstractExecutorService已实现将Runnable用RunnableFuture包裹,达到返回线程执行结果的目的
* shutdown() 不接受新线程,等待其他线程执行完毕
* shutdownNow() 强制关闭所有线程
----
上面讲了那么多,接下来我们回到原点,来看看ThreadPoolExecutor怎样实现4个线程池的
* newCachedThreadPool初始化参数里BlockQueue这个结构是实现缓存最重要的结构,由于newCachedThreadPool使用的是SynchronousQueue<Runnable>而这个队列的特点就是每次只会保留一个在队列内,这也保证了线程之间一定的隔离作用,还有corePoolSize为0,这就导致这样一种场景,有任务来我就不断的夸大线程池,任务完成后因为当前的size肯定大于corePoolSize所以就不断销毁线程
* newFixedThreadPool 这就是固定了corePoolSize,maximumPoolSize不会额外申请新工作线程,LinkedBlockingQueue<Runnable>遵循FIFO排队策略,这是一个按排队策略,拥有固定吞吐量的线程池
* newScheduledThreadPool 这个是单独写了一个类,相对较复杂,BlockQueue用的是DelayedWorkQueue()主要看看ScheduledThreadPoolExecutor的调度策略

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);

在任务进入DelayedWorkQueue时会附带时间参数,只有满足了时间点,该任务才会进入工作线程,从而实现Schedule,纤细请参看[newScheduledThreadPool分析](https://segmentfault.com/a/1190000000395007)
* newSingleThreadExecutor 由于corePoolSize与maximumPoolSize都为1,且BlockQueue为LinkedBlockingQueue所以线程池里永远只会有一个线程且支持FIFO策略
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,612评论 5 471
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,345评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,625评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,022评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,974评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,227评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,688评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,358评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,490评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,402评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,446评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,721评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,802评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,013评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,504评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,080评论 2 341

推荐阅读更多精彩内容