Android线程池

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

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

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

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue(),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(),threadFactory));
}```

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

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,                        
                  long keepAliveTime, TimeUnit unit,BlockingQueue 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 < private static final int SHUTDOWN = 0 < private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 <

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而这个队列的特点就是每次只会保留一个在队列内,这也保证了线程之间一定的隔离作用,还有corePoolSize为0,这就导致这样一种场景,有任务来我就不断的夸大线程池,任务完成后因为当前的size肯定大于corePoolSize所以就不断销毁线程
* newFixedThreadPool 这就是固定了corePoolSize,maximumPoolSize不会额外申请新工作线程,LinkedBlockingQueue遵循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策略

你可能感兴趣的:(Android线程池)