线程池的使用
首先当然要先说线程池的优点。
- 重用线程池中的线程,避免因线程的创建和销毁带来性能开销。
- 能有效控制线程池最大并发数,避免大量线程因互相抢占系统资源而阻塞
- 对线程进行简单管理,并提供定时执行以及制定间隔循环执行能力
ThreadPoolExecutor
构造方法
ThreadPoolExcutor是线程池的实现,其实现了ExecutorService接口,并在构造方法中设置了各种参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
这里一一介绍一下这些参数
corePoolSize
是线程的核心线程数。所谓核心线程是指在线程池中常驻的线程。该类线程即使闲置也不会被回收。因此可以被快速调用。(如果allowCoreThreadTimeOut
属性设置为true
则在超时后也会被终止)
maximumPoolSize
最大线程数,当线程数超过corePoolSize时会开始创建临时线程,而总线程数超过maximumPoolSize时新任务则会被阻塞。
keepAliveTime
非核心线程闲置时间,超过这个时间,则非核心线程会被回收。
unit
用于指定keepAliveTime的单位,是个枚举类。常用的是TimeUnit.MILLISECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等
应导入java.util.concurrent.TimeUnit
workQueue
任务队列,通过execute方法提交的Runnable会储存在队列里。可以设置最大数
threadFactory
县城工厂,为线程池提供创建新线程的功能。ThreadFactor是个接口,有一个方法:Thread newThread(Runnable r)
任务规则
- 若线程池的线程未达到核心线程数量,则直接启动一个核心线程执行任务
- 若线程池中的线程数已经达到或超过核心线程数,则任务被插入到任务队列中排队执行
- 如果在2中无法将任务插入任务队列,则往往是由于队列已经满了。这时候如果线程数量未达到线程池最大值,则立刻启动一个非核心线程
- 若3中线程数量已经达到规定最大值,则拒绝执行
AsyncTask中的线程池
AsyncTask内部是使用线程池来实现功能的。其配置参数如下:
- 核心线程数为CPU核心数+1
Runtime.getRuntime().avaliableProcessors() + 1;
- 线程池最大线程数为CPU核心数的2倍 + 1
Runtime.getRuntime().avaliableProcessors() * 2 + 1;
- 核心线程无超时机制,非核心线程限制时间为1s
int keepAlive = 1;
unit = TimeUnit.SCEONDS;
- 任务队列容量为128
new LinkedBlockingQueue<Runnable>(128);
用Executors创建线程池
线程池分类
用Executors创建的线程池分4类,首先作为测试的代码如下:
主线程
while (true) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringBuilder sb = new StringBuilder()
.append("\n1:").append(runnable1.value)
.append("\n2:").append(runnable2.value)
.append("\n3:").append(runnable3.value)
.append("\n4:").append(runnable4.value)
.append("\n5:").append(runnable5.value);
Log.d("RUNNABLE", sb.toString());
}
测试线程
private class TestRunnable implements Runnable{
private int value;
@Override
public void run() {
while (true) {
value += 10;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (value > 160)
break;
}
}
}
FixedThreadPool
该线程池是一种线程数固定的线程池,其核心线程数即为总核心数,除非线程池关闭,否则所有线程都处于活动状态。同时其任务对列也没有限制
获取方法内部:
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor
(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS
,new LinkedBlockingQueue<Runnable>());
}
测试内容:
int threadCount = 3;
//静态线程池,固定线程数,不会被回收
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(threadCount);
fixedThreadPool.execute(runnable1);
fixedThreadPool.execute(runnable2);
fixedThreadPool.execute(runnable3);
fixedThreadPool.execute(runnable4);
fixedThreadPool.execute(runnable5);
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Bsdwodw-1592182056018)(http://m.qpic.cn/psb?/V10DRhHC2m1DNs/RqKq9j7tj7TQkwVJPpsgzvIK8g0UiZyzrOf67pUMNh8!/b/dDMBAAAAAAAA&bo=QwF9AgAAAAADBx8!&rf=viewer_4)]
CachedThreadPool
该线程池是一种线程数量不确定的线程池。其只有非和新线程,切最大线程数为Integer.MAX_VALUE。相当于没有限制。当线程池中的线程都出于活动时,线程池会创建新的线程来处理新任务。否则会用空闲的线程来处理任务。该线程池的超时市场为60s。其任务队列为一个空基和,是无法插入任何任务。因此所有的人物都将立刻执行。
当无任务时,该线程池内没有任何线程,因此是不占用任何资源的。
方法如下:
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor
(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS
,new SynchronousQueue<Runnable>());
}
测试代码:
//临时线程池,最大线程数为Integer.MAX_VALUE, 所有任务立即执行,适合执行大量耗时较少的任务。没有核心线程
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
cachedThreadPool.execute(runnable1);
cachedThreadPool.execute(runnable2);
cachedThreadPool.execute(runnable3);
cachedThreadPool.execute(runnable4);
cachedThreadPool.execute(runnable5);
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mAP9huK9-1592182056023)(http://m.qpic.cn/psb?/V10DRhHC2m1DNs/JTVColnmdnmfQ5sJpxfEkNpVpJ8H4JY3huiImeHh0!/b/dIMAAAAAAAAA&bo=0QB4AgAAAAADB4k!&rf=viewer_4)]
ScheduledThreadPool
该线程池核心线程数是固定的,而非核心线程数没有限制。当非和新线程闲置时会被立即回收。这类线程池主要用于执行定时任务和具有固定周期的重复任务。
方法如下:
public static ScheduledExecutorService newCachedThreadPool(){
return new ScheduledThreadPoolExecutor
(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS
,new SynchronousQueue<Runnable>());
}
public ScheduledThreadPoolExecutro(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS
,new DelayedWorkQueue());
}
用法如下:
//2000ms 后执行
scheduledThreadPool.schedule(runnable1, 2000, TimeUnit.MILLISECONDS);
//延迟10ms后,每1000ms执行一次
scheduledThreadPool.scheduleAtFixedRate(runnable1, 10, 1000, TimeUnit.MILLISECONDS);
SingleThreadExecutor
该线程池内只有一个核心线程,其确保所有的人物都在同一个线程内按顺序执行。其意义在于统一所有外界任务到一个线程中。使得这些任务不需要处理线程同步的问题。
方法如下:
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorServce
(new ThreadPoolExecutor(1,1
,0L,TimeUnit.MILLISECONDS
,new LinkedBlockingQueue<Runnable>()));
}
用法与前两种相同。