1 为什么需要线程池
一般来说,线程的生命周期需要通过 new(新建)->start(就绪)->running(运行中)->dead(消亡)
假设每个状态到下一个状态需要的时间是
1,2,3 秒
但是我们往往只是关心running 那个状态就可以了,这是线程需要执行的任务
使用线程池,创建一定数量的线程,专门执行任务的run 方法,实现了线程的复用,同时也减少了其他状态的占比,提高了整个系统的运行效率
2 线程池的核心参数
核心线程,最大线程,定义不好解释,还是看这两个参数在执行任务的过程中对worker 线程的创建的影响吧。
这两个参数的目的是:
- 尽量少的创建线程(核心线程是初始化线程池时创建的,当有任务来时,核心线程都忙,则往队列放,而不是立刻创建线程)
- 尽量快的执行任务(优先使用核心线程执行任务,不够,再入队,队满,使用新线程执行)
- 控制最大线程数(资源的限制)
记忆:任务到来,优先使用核心线程,核心线程数不够,再排队,队列排满再开启新线程执行任务,核心线程+非核心线程数达到最大线程数,则将任务交给拒绝策略处理。
3 线程池的分类
FixedExcutor:固定线程池大小
SingleThreadPool:单线程,
CachedThreadPool:可缓存
Executors.newCachedThreadPool();
Executors.newSingleThreadExecutor();//单线程线程池
Executors.newFixedThreadPool(2);//固定线程池大小
4 线程池的底层实现
- 使用阻塞队列去放置未被执行的任务
- worker 线程则是在执行完当前任务后,从队列中取出任务执行
核心构造方法:
ThreadPoolExecutor(int corePoolSize,// 核心线程数
int maximumPoolSize,// 最大线程数
long keepAliveTime,// 线程闲闲置存活时间,如果闲置时间超过改时间,则回收该线程(长时间没任务)
TimeUnit unit,// 时间单位
BlockingQueue<Runnable> workQueue)//任务队列
5阻塞队列的常用方法;
- add/offer 入队,前者队列满时,会抛出异常,后者会返回false,offer 还可以设置时间参数,限时入队
- peek/pool 返回队头元素,前者返回,但是不移除,后者返回且移除
- 读写锁的使用 (ReentrantLock)takeLock,putLock
6 线程池的好处
- 响应快:系统启动就创建一部分线程
- 降低资源消耗:重复利用机制,减少线程重复创建销毁造成的损耗
- 提高线程可管理性:统一分配,调优和监控