- 继承关系
- Executor接口(顶层接口)
- ExecutorService接口
- AbstractExecutorService抽象类
- ThreadPoolExecutor实现类
- ThreadPoolExecutor
- 属性
- 线程池状态(ctl属性)
- 构造器 / 参数
- 线程池运行机制
- 源码级别
- Work类
- Executors工具类提供的线程池实现
- nexFixedThreadPool:固定大小的线程池
- newCachedThreadPool:带缓冲池的线程池
- newSingleThreadExecutor:单线程线程池
- 其他
- submit() / invokeAny() / invokeAll()使用示例
4.1 继承关系
图片
- Executor接口:顶层接口,只定义了一个execute(Runnable)方法
void execute(Runnable command) |
|
- ExecutorService接口:继承了Executor接口,定义了三种方法:生命周期相关方法,submit()方法,invokeAll() / invokeAny()
生命周期管理的相关方法 (只定义接口方法,实现方法全部由ThreadPoolExecutor实现) |
|
|
|
|
|
|
|
|
|
|
|
submit()方法 (只定义接口方法,实现方法全部由AbstractExecutorService实现) |
|
|
invokeAll() / invokeAny() (只定义接口方法,实现方法全部由AbstractExecutorService实现) |
|
提交tasks中所有任务 |
|
提交tasks中所有任务,哪个先执行完哪个先返回结果,其他任务取消 |
- AbstractExecutorService抽象类:继承了ExecutorService接口
- 没什么特别的,实现了submit()方法,invokeAll() / invokeAny()方法,见上
- ThreadPoolExecutor实现类:继承AbstractExecutorService抽象类,最常用的线程池实现类,下文分析
4.2 ThreadPoolExecutor
- 属性
ctl |
|
|
worker相关 |
|
|
|
||
锁相关 |
|
|
构造器参数相关 |
|
|
静态内部类 |
|
|
监控线程池的属性 |
|
|
- 线程池状态
- ThreadPoolExecutor使用AtomicInteger ctl高3位来表示线程池状态,低29位表示线程池数量
-
状态名 高3位 接受新任务 处理阻塞队列任务 说明 RUNNING 111 Y Y 刚创建线程池默认状态 SHUTDOWN 000 N Y 不会接受新任务,但会处理阻塞队列剩余任务 STOP 001 N N 会中断正在执行的任务,并丢弃阻塞队列中的任务 TIDYING 010 / / 任务全部执行完毕,活动线程为0即进入终结 TERMINATED 011 / / 终结状态
-
- 为何要把线程池状态(3bits)和数量(29bits)合并在一起:保证原子性,即可以只用1次CAS操作进行赋值
- 从数字上比较TERMINATED(3)>TIDYING(2)>STOP(1)>SHUTDOWN(0)>RUNNING(-1)
- ThreadPoolExecutor使用AtomicInteger ctl高3位来表示线程池状态,低29位表示线程池数量
- 构造器 / 参数
-
public ThreadPoolExecutor( int corePoolSize, //核心线程数目(最多保留的线程数) int maxmumPoolSize, //最大线程数 long keepAliveTime, //生存时间-针对救急线程 TimeUnit unit, //时间单位-针对救急线程 BlockingQueue<Runnable> workQueue, //阻塞队列 ThreadFactory threadfactory, //线程工厂-可以为线程取个好名字 RejectedExecutionHandler handler) //拒绝策略
-
- 线程池运行机制
- 1. 线程池刚开始没有线程,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
- 2. 当线程数达到corePoolSize并且没有线程空闲,这时再加入新任务,新任务会进入workQueue进行排队,直到有空闲线程。
- 3. 如果队列选择了有界队列,那么任务超过了队列大小时候,会创建maximumPoolSize- corePoolSize数目的线程来救急
- 4. 如果线程到达maximumPoolSize仍然有新任务,这时会进行拒绝策略,jdk提供了4种实现
- AbortPolicy(默认策略):让调用者抛出RejectedExecutionException异常,
- CallerRunsPolicy:让调用者执行任务
- DiscardPolicy:放弃本次任务
- DiscardOldestPolicy:放弃队列中最早的任务,本任务取代之
- 5. 当高峰过去后,超过corePoolSize的急救线程如果一段时间没有任务,需要结束节省资源,时间由keepAliveTime和unit控制
- Worker类
- 待更新
4.3 Executors工具类提供的线程池实现
JUC tools下的Executors类提供了三个线程池
(1)nexFixedThreadPool:固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor( nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.executor(()->{
//任务1
});
pool.executor(()->{
//任务2
});
pool.executor(()->{
//任务3
});
- 特点
- 核心线程数=最大线程数,即没有救急线程,因此也无需超时时间
- 阻塞队列是无界的,可以放任意数量的任务
- 评价
- 适用于任务数量已知,相对耗时的任务
(2)newCachedThreadPool:带缓冲池的线程池
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 特点
- 核心线程数是0,最大线程数MAX_VALUE,即所有创建的线程都是救急线程(60s后可以回收),救急线程可以无限创建。
- 队列采用了SychronousQueue,实现特点是:没有容量,没有线程来取得时候是放不进去的。
- 评价
- 整个线程池表现为线程数会根据任务量不断增长,没有上限。当任务执行完毕,空闲线程1分钟后会释放。
- 适合任务数较为密集,但每个任务执行时间较短的情况
(3)newSingleThreadExecutor:单线程线程池
- 特点
- 希望多个任务排队执行。线程数固定为1,当任务大于1时,会进入无界队列排队。执行完毕后,这唯一的线程也不会释放
- Executors.newSingleThreadExecutor()的线程个数始终为1,不能修改
- 它运用了装饰器模式,返回的是FinalizableDelegatedExecutorService对象不能调用ThreadPoolExecutor中特有的方法
- 区别
- 对比自己创建的串行执行的单线程任务,如果任务失败会终止。而该线程池会继续往下执行
- 对比Executors.newFixedThreadPool(1),可以用setCorePoolSize来修改数量
4.4 其他
(1)submit示例:
ExecutorService pool = Executors.nexFixedThreadPool(2);
//实例1:submit方法中提供Callable类
Future<String> future = pool.submit(new Callable<String>(){
@Override
public String call() throws Exception{
Thread.sleep(1);
return "123456789";
}
});
//实例2:利用lambda函数
Future<String> future1 = pool.submit(()->{
Thread.sleep(1);
return "987654321";
});
//利用future.get()方法获取返回值
String ans = future.get();
String ans1 = future1.get();
(2)invokeAll()示例:
ExecutorService pool = Executors.newFixedThreadPool(2);
List<Future<String>> futures = pool.invokeAll(Arrays.asList(
() -> {
Thread.sleep(1);
return "线程1";
},
() -> {
Thread.sleep(1);
return "线程2";
},
() -> {
Thread.sleep(1);
return "线程3";
}
));
for(Future<String> f: futures){
try{
System.out.println(f.get());
}catch(InterruptedException | ExecutionException e){
e.printStackTrace();
}
}
(3)invokeAny()示例:
ExecutorService pool = Executors.newFixedThreadPool(3);
String str = pool.invokeAny(Arrays.asList(
() -> {
Thread.sleep(3);
return "线程1";
},
() -> {
Thread.sleep(2);
return "线程2";
},
() -> {
Thread.sleep(1);
return "线程3";
}
));
//str = 线程2;因为线程2先执行完;线程1和线程3会被取消