线程池的使用场景
在安卓开发中经常会遇到处理多张图片,或者分片上传等需要使用多线程的情况,如果简单的通过new Thread创建线程,会造成线程的频繁创建和销毁,占用大量的资源。并且频繁的调用GC机制,这会使性能降低,又非常耗时。
常见的线程池分类
最基本的ThreadPoolExecutor
1 通过构造方法,创建最基本的线程池,这也是参数最多,允许用户自己设置
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(
2,//核心线程数量
4,//该线程池内最大线程数量
100,//非核心线程保活时间
TimeUnit.SECONDS,//非核心线程保活时间单位
new LinkedBlockingDeque<Runnable>(2),//线程队列(队列中允许最大线程数量)
new ThreadPoolExecutor.AbortPolicy()//线程数量超过限制时,拒绝策略。默认抛出异常
);
2 创建线程池之后,为了方便查看当前线程的信息,自定义一个线程
class MyThread implements Runnable {
String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("syw","线程:"+Thread.currentThread().getName() +" 执行:"+name +" run");
}
}
3 使用线程池的方法,执行多个任务
// 2个核心线程
// 6-2=4个任务将进入LinkedBlockingDeque队列,但是队列只能排进2个,所以新创建2个线程
// 核心线程数2+新创建的线程2如果超过线程池内最大线程数,在当前拒绝策略下会抛出异常
for (int i = 0; i < 6; i++) {
Log.e("syw","添加第"+i+"个任务");
threadPoolExecutor.execute(new MyThread("线程"+i));
Iterator iterator = threadPoolExecutor.getQueue().iterator();
while (iterator.hasNext()){
MyThread thread = (MyThread) iterator.next();
Log.e("syw","列表:"+thread.name);
}
}
4 执行结果
5 结果分析
- 线程池内核心线程数2,最大线程数4,队列中最大线程数量2
- 先后顺序:核心线程、线程队列、创建新线程
- 线程0和线程1直接分配核心线程执行,线程队列最大数量为2,所以线程2和线程3则需要添加到队列中排队。到线程4和线程5的时候,就需要另外创建非核心线程执行了。2个核心线程+2个非核心线程等于最大线程数4。此时如果for循环的数量是7的话,也就是说还需要创建3个非核心线程,此时会超出该线程池最大线程数,在当前的拒绝策略下,会抛出异常。
6 执行顺序
首先是核心线程1和核心线程2立刻执行线程0、线程1。
然后线程2、线程3进入队列,等待核心线程执行完成。
新创建的非核心线程3和非核心线程4立刻执行线程4、线程5
所以执行的顺序是0-1-4-5-2-3(线程的执行顺序是随机的0/1可能互换,4/5可能互换,2/3可能互换)
拒绝策略
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,//核心线程数量
4,//该线程池内最大线程数量
100,//非核心线程保活时间
TimeUnit.SECONDS,//非核心线程保活时间单位
new LinkedBlockingDeque<Runnable>(2),//线程队列(队列中允许最大线程数量)
// new ThreadPoolExecutor.AbortPolicy()//默认策略。线程数量超过限制时,拒绝策略,抛出异常
// new ThreadPoolExecutor.DiscardPolicy()//线程数量超过限制时,丢掉多余任务
// new ThreadPoolExecutor.DiscardOldestPolicy()//线程数量超过限制时,丢掉先进入队列的任务
// new ThreadPoolExecutor.CallerRunsPolicy()//线程数量超过限制时,添加任务队列失败,主线程执行该任务。然后才会继续添加任务
);
如果线程数超过6(最大线程数4+队列最大值2)时
- 第一种策略在添加第7个线程时,直接抛出异常
- 第二种策略忽略第6个以后的线程,不抛异常
- 第三种策略先移除队列中的第一个线程,然后在队列的尾部加入一个新线程。
- 第四种策略相当于在线程池中加入了一个主线程,由主线程处理核心线程–线程队列–非核心线程之外的那一个任务。此时线程池中所有线程都被占用,不再继续添加任务。核心线程/非核心线程/主线程执行任务,当有线程执行完,线程队列中任务取出执行,后续的线程再加入队列。(只有当核心线程/非核心线程/队列都满的时候,主线程才会出来执行任务)
- 第四种策略执行图例