小伙伴们对于Thread、Runnable、Handler、AyscTask应该十分熟悉,并且借助它们完成了一个个炫酷的功能,那么关于线程池的了解呢?这边文章就和小伙伴一起来看看关于线程池的这些事
1.什么是线程池
线程池是1块缓存了一定线程数量的区域,用来复用线程和管理线程,如统一分配、调优、监控和控制最大并发数等
优点:
- 降低因线程创建销毁所带来的性能开销 重用缓存线程池的线程
- 提高线程响应速度和执行效率 通过重用线程 不需要创建线程即可马上执行 优化线程的执行顺序 避免大量线程因互相抢占系统资源而导致的阻塞现象
2.工作原理
线程中有6个核心参数
- corePoolSize : 核心线程数 ,默认情况下,核心线程会一直存活(包括空闲状态)
- maximumPoolSize:线程池所能容纳的最大线程数,当活动线程数达到该数值后,后续新的任务将会阻塞
- keepAliveTime:非核心线程 限制超时时长,超过该时长,非核心线程就会被回收
- unit:指定 keepAliveTime参数的时间单位,毫秒、秒等
- workQueue:任务队列,通过线程池的execute方法提交的Runnable对象,将存储在该参数中
- threadFactory:线程工厂(接口)用来为线程池创建新的线程 Thread newThread(Runnable r)
ThreadPoolExecutor是线程池的真正实现类
// 创建线程池对象如下
// 通过 构造方法 配置核心参数
Executor executor = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory
);
// 构造函数源码分析
public ThreadPoolExecutor (int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable workQueue>,
ThreadFactory threadFactory )
Java里内置了4中常用的线程池(即配置好核心参数的线程池)
处理任务的优先级:核心线程 > 任务队列 > 最大线程
当一个任务需要线程池去执行的时候,首先
判断线程池中的线程数是否大于核心线程数,如果小于,就在核心线程里创建一个线程执行任务
如果大于
判断任务队列是否已满,如果没有满,任务被放到任务队列(等待执行)
如果满了
判断线程池中的线程数是否大于最大线程数,如果小于,就在非核心线程里创建一个线程执行任务
如果大于
表示该任务无法执行 通过handler指定的策略处理任务
特别说明:当线程池中的线程数量 > 核心线程数时,若某非核心线程的空闲时间大于keepAliveTime,线程将被终止,通过这样的策略,线程池可以动态的调整线程中的线程数
3.使用流程
// 1. 创建线程池
// 创建时,通过配置线程池的参数,从而实现自己所需的线程池
Executor threadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory
);
// 注:在Java中,已内置4种常见线程池,下面会详细说明
// 2. 向线程池提交任务:execute()
// 说明:传入 Runnable对象
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 线程执行任务
}
});
// 3. 关闭线程池shutdown()
threadPool.shutdown();
// 关闭线程的原理
// a. 遍历线程池中的所有工作线程
// b. 逐个调用线程的interrupt()中断线程(注:无法响应中断的任务可能永远无法终止)
// 也可调用shutdownNow()关闭线程:threadPool.shutdownNow()
// 二者区别:
// shutdown:设置 线程池的状态 为 SHUTDOWN,然后中断所有没有正在执行任务的线程
// shutdownNow:设置 线程池的状态 为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
// 使用建议:一般调用shutdown()关闭线程池;若任务不一定要执行完,则调用shutdownNow()
4.常见的5种线程池
- 定长线程池(FixedThreadPool)
- 定时线程池(
ScheduledThreadPool
) - 可缓存线程池(CachedThreadPool)
- 单线程化线程池(SingleThreadExecutor)
- 耗时任务线程池(WorkStealingPool)
1) 定长线程池(FixedThreadPool)
特点:只有核心线程不会被回收、线程数量固定、任务队列无大小限制(超出的线程任务会在队列中等待)
应用场景:控制线程线程的最大并发数
具体使用:通过Executors.newFixedThreadPool()创建
// 1. 创建定长线程池对象 & 设置线程池线程数量固定为3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
fixedThreadPool.execute(task);
// 4. 关闭线程池
fixedThreadPool.shutdown();
2)定时线程池(ScheduledThreadPool)
特点:核心线程数量固定、非核心线程数数量无限制(闲置时马上回收)
应用场景:执行定时/周期性任务
使用:通过Executors.newScheduledThreadPool()创建
// 1. 创建 定时线程池对象 & 设置线程池线程数量固定为5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:schedule()
scheduledThreadPool.schedule(task, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务
// 4. 关闭线程池
scheduledThreadPool.shutdown();
3)可缓存线程池(CachedTheadPool)
特点:只有非核心线程、线程数量不固定(可无限大)、灵活回收空闲线程(具备超时机制,全部回收时几乎不占系统资源)、新建线程(无线程可用时)
任何线程任务到来都会立刻执行,不需要等待
应用场景:执行大量、耗时少的线程任务
使用:通过Executors.newCachedTheadPool()创建
// 1. 创建可缓存线程池对象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
cachedThreadPool.execute(task);
// 4. 关闭线程池
cachedThreadPool.shutdown();
//当执行第二个任务时第一个任务已经完成
//那么会复用执行第一个任务的线程,而不用每次新建线程。
4)单线程化线程池(SingleThreadExecutor)
特点:只有一个核心线程(保证所有任务按照指定顺序在一个线程中执行,不需要处理线程同步的问题)
应用场景:不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作,文件操作等
使用:通过Executors.newSingleThreadExecutor()创建
// 1. 创建单线程化线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
singleThreadExecutor.execute(task);
// 4. 关闭线程池
singleThreadExecutor.shutdown();
5)耗时任务线程池(WorkStealingPool)
WorkStealingPool适合使用在很耗时的操作,但是WorkStealingPool不是ThreadPoolExecutor的扩展,它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executors类中实现,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中
// 1. 创建单线程化线程池
ExecutorService workStealingPool = Executors.newWorkStealingPool();
// 2. 创建好Runnable类线程对象 & 需执行的任务
Runnable task =new Runnable(){
public void run(){
System.out.println("执行任务啦");
}
};
// 3. 向线程池提交任务:execute()
workStealingPool.execute(task);
// 4. 关闭线程池
workStealingPool.shutdown();
}