为什么引入线层池概念? 如果短时间内新建大量的线层,因为线程的创建与销毁的资源开销是非常大的,大量的子线程会分享主线程的系统资源,从而会使主线程因资源受限而导致应用性能降低。用线程池去管理这堆线层,统一的分配,调优和监控。它是为提升APP性能而存在的。
1.Java通过Executors提供四种线程池:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
示例:
ExecutorService executor = Executors.newFixedThreadPool(5);
for(inti = 0; i < 20; i++) {
Runnable worker = newWorkerThread(""+ i);
executor.execute(worker);
}
executor.shutdown();
while(!executor.isTerminated()) {
}
2.Executors 类使用 ExecutorService 提供的ThreadPoolExecutor参数解析
ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,milliseconds,runnableTaskQueue,ThreadFactory,handler)
共六个参数:
共六个参数:
corePoolSize: 线层池的基本大小。
maximumPoolSize: 线程池允许创建的最大线程数,当runnableTaskQueue(任务队列)满了的时候,线层池大小会超出corePoolSize,达到最大值。
keepAliveTime: 线程活动保持时间,线程池的工作线程空闲后,保持存活的时间。
TimeUnit: 线程活动保持时间的单位可选的单位,天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
RunnableTaskQueue: 任务队列 用于保存等待执行的任务的阻塞队列。
ThreadFactory: 线层工厂 给线程命名、是否为用户线程等等属性
。
RejectedExecutionHandler: 当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。
示例:
ThreadFactory threadFactory = Executors.defaultThreadFactory();
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5,10,10, TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(5),threadFactory,rejectedExecutionHandler);
for (int i=1;i<=20;i++) {
Runnable runnable=new WorkThread(threadPoolExecutor,i*2000,i);
threadPoolExecutor.execute(runnable);
}
threadPoolExecutor.shutdown();
}
RejectedExecutionHandler rejectedExecutionHandler=new RejectedExecutionHandler(){
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
Log.e("RejectedHandler","转移到:RejectedExecutionHandler"+r.toString());
}
};
public class WorkThread implements Runnable {
private int i;
private ThreadPoolExecutor threadPoolExecutor;
private int delay;
public WorkThread(int i){
this.i=i;
}
public WorkThread(ThreadPoolExecutor threadPoolExecutor,int delay,int i){
this.threadPoolExecutor=threadPoolExecutor;
this.delay=delay;
this.i=i;
}
@Override
public void run() {
try {
Log.e("WorkThread", "在执行第" + i + "线程");
Thread.sleep(delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Logcat:
示例中,任务队列大小是5,只会存储5个阻塞线程,线程到达10个后,任务队列不再存储线程,线程池开始超出容量,线程数到达20个时达到maximumPoolSize,然后就往rejectedExecutionHandler转移。
如上图所示:ArrayBlockingQueue 是先进先出的的原则,11-15个线程是超出线层池基本容量后达到最大容量的。6-10是阻塞放在任务列的,6-10线程会在后面执行。其中16-20线程转移到RejectedExecutionHandler 处理。
3.线程池的关闭方法
shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务;
shutdownNow():立即终止线程池,并尝试打断正在执行的任务(测试过,线程里的任务还是会执行下去的),并且清空任务缓存队列,返回尚未执行的任务。重新使用必须先初始化,否则会报错。
在Activity关闭的时候要关闭线程池,不然很容易造成内存泄露。
4.AsyncTask内部的线程池封装学习
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128);
/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
这个 轻量级的抽象异步类封装了静态的线程池,设置了基本大小是可用处理器数量加1(CUP_COUNT+1),最大数量是CUP数的两倍。据说这个是根据线程的计算公式:
线程数=CPU核心数/(1-阻塞系数),最佳线程个数一般在N和N+1之间选择。(度娘了一下=-=)。线程池并发太大,CPU都HOLD不住了。。。
然后任务队列使用了LinkedBlockingQueue 最大为128的一个基于链表结构的阻塞队列,此队列按 FIFO (先进先出) 排序元素。
最后看看ThreadFactory 线程工厂这个接口。AtomicInteger 是一种线程安全的加减操作接口,保证线程安全有序的给线程命名咯。
当然这只是一部分,AsyncTask内部还有很多东西值得借鉴学习。