线程池
有关线程池的创建、常见的几种线程池,线程池的使用方式
创建线程池
创建线程池:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
各个参数含义
参数 | 含义 |
---|---|
corePoolSize | 核心线程数 |
maximumPoolSize | 最大线程数 |
keepAliveTime和unit | 空闲存活时间 |
workQueue | 任务缓存队列 |
threadFactory | 线程工厂 |
handler | 拒绝策略 |
线程池的执行流程为、
- 线程池中的线程未达到核心线程,提交任务后就会立刻执行任务
- 线程池中的线程达到核心线程数,将任务提交到任务缓存队列
- 任务缓存队列已满,未达到最大线程数,将会创建新的线程执行任务
- 达到最大线程数,执行拒绝策略
- 拒绝策略类型
初始化 | 特性 |
---|---|
ThreadPoolExecutor.AbortPolicy() | 抛出异常 |
ThreadPoolExecutor.DiscardPolicy() | 直接丢弃不抛出异常 |
ThreadPoolExecutor.DiscardOldestPolicy() | 丢弃队列中存在最长的时间 |
ThreadPoolExecutor.CallerRunsPolicy() | 由提交任务的线程来执行任务 |
ThreadPoolExecutor.CallerRunsPolicy() 这种拒绝策略比较合理,通过将任务由提交任务的线程去执行这样可以减少任务的累积
Executers.new
初始化 | 特性 |
---|---|
Executors.newCachedThreadPool() | 无上限的线程数的线程池,线程会保持60s |
Executors.newFixedThreadPool() | 固定线程数,无上限的缓存任务队列 |
Executors.newSingleThreadExecutor() | 单个线程,无上限的缓存任务队列 |
Executors.ScheduledThreadPoolExecutor() | 有固定线程数,延迟队列 |
ForkJoinPool
ForkJoinPool线程池
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask task = forkJoinPool.submit(new Fibonacci(i));
task.get();
阻塞队列
初始化 | 特性 |
---|---|
LinkedBlockingQueue | 无容量上限的队列 |
SynchronousQueue | 无容量的队列,提交任务立即弹出 |
DelayedWorkQueue | 延迟队列 |
核心线程数的选择
核心线程数的选择是根据任务的类型CPU密集性、IO密集型
CPU密集性任务,会一直使用cpu进行计算,合适的线程数为cpu核心数的1~2倍,过多的线程数会造成线程上下文的频繁切换,执行效率反而不高。
IO密集型任务,会执行IO任务,cpu进行等待,因此线程数可以尽量多一些。
*线程数 = CPU核心数 (1 + 平均等待时间/平均工作时间)
shutdown和shutdownNew的区别
shutdown不在允许提交任务,待执行线程池中正在执行的任务和任务队列中的任务后线程池关闭
shutdownNow立即暂停任务,向正在执行任务的线程发送中断信号,将等待队列中的任务转移出来,然后线程池关闭
线程池复用线程的逻辑是,创建线程后不停的获取task进行执行(run)方法