线程池(ThreadPool):一种线程使用模式。
What?不(什)知(么)所(东)谓(东)。那么首先我们得了解什么事线程,线程一般理解为我们顺序执行某一任务的最小执行单元,同一时间只做一件事情。而线程的创建和销毁在系统里面是有开销的,而且还比较大。
我们来想想我们使用线程的方式一般为new Thread() 这样做是有很多弊端的,1、创建线程是有开销的 2、线程的生命周期不可控,容易引起内存泄漏 3、线程使用混乱,最后自己创建了多少线程不清楚,无法有效的维护。而线程池的目的就是帮我们有效的维护我们的线程,同时尽量减少线程的创建、销毁的开销。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize:核心线程池大小
maximumPoolSize:最大线程池大小
keepAliveTime:线程池最大空闲时间
unit:时间单位
workQueue:线程等待队列
threadFactory:线程创建工厂
handler:拒绝策略
我们可以用ThreadPoolExecutor来创建线程池,也可以用Executors的静态实现类创建我们的线程池。
1、Executors.newFixedThreadPool(N)
创建有N条核心线程没有非核心线程的线程池,核心线程不会被回收。当超过N数量的线程需排队等待前面线程执行完毕方可执行。
private void startTheadPool(){
//通过Executors.newFixedThreadPool(N)创建的线程有N条固定数量线程的线程池,仅有核心线程且没有超时策略,线程不会被回收。
//该方法创建的线程池没有非核心线程 当超过N数量的线程会等待前面线程执行完才会执行
ExecutorService executorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CFixedThreadPool Start Run: 任务 = "+index+",线程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
executorService.execute(runnable);
}
}
执行结果如下:
2019-08-13 15:26:44.674 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 0,线程 = pool-1-thread-1
2019-08-13 15:26:44.675 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 1,线程 = pool-1-thread-2
2019-08-13 15:26:46.676 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 2,线程 = pool-1-thread-1
2019-08-13 15:26:46.676 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 3,线程 = pool-1-thread-2
2019-08-13 15:26:48.677 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 4,线程 = pool-1-thread-1
2019-08-13 15:26:48.677 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 5,线程 = pool-1-thread-2
2019-08-13 15:26:50.679 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 7,线程 = pool-1-thread-2
2019-08-13 15:26:50.679 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 6,线程 = pool-1-thread-1
2019-08-13 15:26:52.680 21796-21851/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 9,线程 = pool-1-thread-2
2019-08-13 15:26:52.680 21796-21850/com.calm.cthreadpool E/CFixedThreadPool: CFixedThreadPool Start Run: 任务 = 8,线程 = pool-1-thread-1
可以看到一次执行了2条线程,而2秒之后待前2条线程执行完毕才继续执行下2条线程。而线程始终为线程池里的thread-1和thread-2。
2、Executors.newCachedThreadPool()
该方法创建的线程只有非核心线程,数量为无限大。没有核心线程。适合做一些量比较大但耗时较短的操作。
private void startTheadPool(){
//CachedThreadPool是一种线程数量不定的线程池,只有非核心线程,线程数量为无限大。
//比较适合做一些大量的耗时较少的任务
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CCachedThreadPool Start Run: 任务 = "+index+",线程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
executorService.execute(runnable);
}
}
执行结果如下:
2019-08-13 15:34:00.398 21796-22119/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 0,线程 = pool-2-thread-1
2019-08-13 15:34:00.401 21796-22120/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 1,线程 = pool-2-thread-2
2019-08-13 15:34:00.403 21796-22121/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 2,线程 = pool-2-thread-3
2019-08-13 15:34:00.405 21796-22122/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 3,线程 = pool-2-thread-4
2019-08-13 15:34:00.405 21796-22123/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 4,线程 = pool-2-thread-5
2019-08-13 15:34:00.407 21796-22125/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 6,线程 = pool-2-thread-7
2019-08-13 15:34:00.408 21796-22126/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 7,线程 = pool-2-thread-8
2019-08-13 15:34:00.409 21796-22127/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 8,线程 = pool-2-thread-9
2019-08-13 15:34:00.413 21796-22128/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 9,线程 = pool-2-thread-10
2019-08-13 15:34:00.418 21796-22124/com.calm.cthreadpool E/CCachedThreadPool: CCachedThreadPool Start Run: 任务 = 5,线程 = pool-2-thread-6
我们可以看到基本在同一时刻执行了所有的线程,并且执行的线程均未重复。
3、Executors.newScheduledThreadPool(N)
该方法创建N条核心线程和不限制的非核心线程的线程池。一般来说,该线程池执行的为带有一定延迟或周期调度的线程任务。
private void startTheadPool(){
//ScheduledExecutorService(N) 有N条核心线程且有非限制的非核心线程,非核心线程闲置时会被立即回收。
//主要用于执行定时任务和具有固定周期的重复任务
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CScheduleThreadPool Start Run: 任务 = "+index+",线程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
//延迟2秒执行runnable
executorService.schedule(runnable,2000, TimeUnit.MILLISECONDS);
}
}
执行效果为线程开始后延迟2秒之后才开始真正的执行线程里的内容。除了延迟,其效果与FixedThreadPool执行效果类似。
4、Executors.newSingleThreadExecutor()
创建一个只有唯一线程的线程池。所有任务均顺序等待该唯一线程的执行。因为只有唯一线程,所以我们也不用去考虑线程通过的问题。
private void startTheadPool(){
//SingleTheadExecutor只有一个核心线程,无非核心线程 所有任务都在一个线程里顺序执行 不需要线程同步
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++){
final int index = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
Log.e(TAG,"CSingleThreadExecutor Start Run: 任务 = "+index+",线程 = "+Thread.currentThread().getName());
try {
Thread.sleep(2000);
}catch (InterruptedException e){
}
}
};
executorService.execute(runnable);
}
}
因为只有一个唯一线程,所以我们会每2秒执行一个任务,且线程名只有唯一的一个。看下验证结果。
2019-08-13 15:44:44.175 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 0,线程 = pool-3-thread-1
2019-08-13 15:44:46.177 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 1,线程 = pool-3-thread-1
2019-08-13 15:44:48.180 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 2,线程 = pool-3-thread-1
2019-08-13 15:44:50.180 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 3,线程 = pool-3-thread-1
2019-08-13 15:44:52.182 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 4,线程 = pool-3-thread-1
2019-08-13 15:44:54.185 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 5,线程 = pool-3-thread-1
2019-08-13 15:44:56.186 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 6,线程 = pool-3-thread-1
2019-08-13 15:44:58.188 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 7,线程 = pool-3-thread-1
2019-08-13 15:45:00.190 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 8,线程 = pool-3-thread-1
2019-08-13 15:45:02.191 21796-22542/com.calm.cthreadpool E/CSingleThreadExecutor: CSingleThreadExecutor Start Run: 任务 = 9,线程 = pool-3-thread-1
可以看到与我们预判一致,没隔2秒执行一个任务,执行的线程名为thread-1
以上4种方式 就是我们线程池的常规使用方案,在实际项目中我们可以根据实际场景来择优使用。