Android线程池
一、为什么要用线程池
目前就我在Android开发工作中,实际自己写线程用到线程池真的很少,但是又不得不去了解下线程池。线程池直白点就是管理线程的一个工具,平时我们创建子线程可能直接用Thread方法创建一个子线程就完事了。
Thread{
//需要执行的耗时代码
}.start()
当然,如果是简单的单线程,不需要使用线程池的。牵扯多线程时推荐试用线程池,比如我们常见的多个文件同时下载。会牵扯到多个线程创建执行,如果处理不好,会导致大量线程创建和销毁,这样会造成资源浪费,使性能降低。这时候就需要用到线程池去管理线程
二、线程池原理
线程池是一个池子,里面能容纳多个线程。线程池帮助我们管理线程的创建、复用、销毁;具体啥时候创建,何时复用,什么时候销毁;需要根据ThreadPoolExecutor
类的构造参数去实现
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
参数说明:
-
corePoolSize
: 核心线程数量,存活情况有两种:1.当allowCoreThreadTimeout = flase (默认)时 ,核心线程会伴随线程池的整个生命周期一直存活;2.当allowCoreThreadTimeout = true 时,空闲时长超过 keepAliveTime设置的时长,会被销毁。 -
maximumPoolSize
:最大线程数,可以理解为线程池容纳线程最大个数。 -
keepAliveTime
:允许空闲时长,超出这个时长,非核心线程会被销毁(非核心线程 = maximumPoolSize - corePoolSize),核心线程会不会被销毁还需要看参数allowCoreThreadTimeout是否为true -
unit
:时间单位,与keepAliveTime参数有关 -
workQueue
:任务队列,线程池中的线程没有空闲时,多余的任务会放任务队列中等待
下面代码是一个实例:
val threadPoolExecutor =
ThreadPoolExecutor(2, 4, 1, TimeUnit.SECONDS, LinkedBlockingQueue<Runnable>(6))
findViewById<View>(R.id.btn5).setOnClickListener{
for (i in 1..10) {
threadPoolExecutor.execute{
Thread.sleep(3000)
Log.e("print","当前线程个数:$i, 线程名:${Thread.currentThread().name}")
}
}
}
核心线程数为2,最大线程数为4,超时1秒,任务队列长度6个。任务总数10个
注意:这里有个公式(任务队列总数 + 最大线程数 >= 任务总数),这个公式必须成立,不然会报错java.util.concurrent.RejectedExecutionException
运行结果:
log1
等待几秒再运行for循环中代码打印日志
log2
分析日志1:
一共10个任务,程序会先创建2个核心线程去领2个任务执行,还剩下8个任务。而任务队列长度只有6,还有剩余两个任务没法放入,这时候就会创建非核心线程去执行这两个任务(这里允许最大线程数为4,已经创建了2个核心线程,刚好还可以创建2个非核心线程,也就是我前面说的那个公式必须成立,如果这里最大线程数小于4,就会报错)。看日志刚好一共创建了4条线程,后面的都是复用这4条线程去执行
分析日志2:
因为等待了几秒,超过了我们设置的超时时间1秒。线程池会回收两个非核心线程。从日志可以看出线程1和3被回收了(非核心线程),复用了线程2和4(核心线程),然后又创建了两个新的线程5和6
三、介绍几种常见线程池
- Executors.newFixedThreadPool(2) 核心线程池,用此方法创建的线程池只有核心线程
- Executors.newCachedThreadPool() 非核心线程池,用此方法创建的线程池只有非核心线程
- Executors.newSingleThreadExecutor() 单线程池,用此方法创建的线程池只有一个核心线程
- Executors.newScheduledThreadPool(5) 调度线程池,用此方法创建的线程池可设置核心线程,非核心线程为最大值
几种方式底层都是实现ThreadPoolExecutor
构造参数