1、 ThreadPoolExecutor谈谈你的理解?
- ①. 为什么使用线程池,优势?
- 线程池做的工作主要是控制运行的线程的数量,处理过程中将任务加入队列,然后在线程创建后启动这些任务,如果显示超过了最大数量,超出的数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行
- 它的主要特点为:线程复用 | 控制最大并发数 | 管理线程.
- ②. 线程池如何使用(Java中的线程池是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个类)
-
③. 方法详解与代码实现
前三个方法(掌握)
(1).Executors.newFixedThreadPool(int) : 一池定线程主要特点:
1、创建一个定长线程池,可控制线程的最大并发数,超出的线程会在队列中等待
2、newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize是相同的,它使用的是LinkedBlockingQueue
(2).Executors.newSingleThreadExecutor( ) : 一池一线程
主要特点:
1、创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务都按照指定顺序执行
2、newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1,它使用的是LinkedBlockingQueue
(3).Executors.newCachedThreadPool( ) : 一池N线程
主要特点:
1、创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则创建新线程
2、newCachedThreadPool的corePoolSize为0,maximumPoolSize为Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是说来了任务创建线程运行,如果线程空闲超过60秒,就销毁线程
(4).Executors.newScheduledThreadPool():基本不使用
/
//看cpu的核数
//System.out.println(Runtime.getRuntime().availableProcessors());
* 第四种获取/使用java多线程的方式,线程池
* */
public class ExecutorTest {
public static void main(String[] args) {
ExecutorService threadPool= Executors.newFixedThreadPool(5);//一池5个处理线程
// ExecutorService threadPool=Executors.newSingleThreadExecutor();//一池一线程
// ExecutorService threadPool = Executors.newCachedThreadPool();//一池N线程
System.out.println(Runtime.getRuntime().availableProcessors());
try {
for (int i = 1; i <= 10; i++) {
//使用
// threadPool.execute(() -> {
// //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
// System.out.println(Thread.currentThread().getName() + "\t 办理业务~!");
// });
//这里也可以传入对应的类,这个类实现了Runnable接口
threadPool.execute(new BatchTackRunnable());
//try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) {e.printStackTrace();}
}
}catch (Exception e){
}finally {
//关闭
threadPool.shutdown();
}
}
}
class BatchTackRunnable implements Runnable{
@Override
public void run() {
//模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程
System.out.println(Thread.currentThread().getName() + "\t 办理业务~!");
}
}
2、 线程池的七大参数
- ①. corePoolSize (核心线程数):任务队列未达到队列容量时,最大可以同时运行的线程数量。
- 在创建了线程池后,当有请求任务来之后,就会安排池中的线程去执行请求任务,近似理解为今日当值线程
- 当线程池中的线程数目达到corePoolSize后,就会把到达的任务放入到缓存队列当中
-
②. maximumPoolSize:任务队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
-
③. keepAliveTime:线程池中的线程数量大于
corePoolSize
的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime
才会被回收销毁。(销毁非核心线程) -
④. unit:keepAliveTime的单位
-
⑤. workQueue:新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。
-
⑥. threadFactory:表示生成线程池中工作线程的线程工厂,用户创建新线程,一般用默认即可(银行网站的logo | 工作人员的制服 | 胸卡等)
-
⑦. handler:饱和策略,表示当线程队列满了并且工作线程大于等于线程池的最大显示数(maxnumPoolSize)时如何来拒绝
ThreadPoolExecutor
饱和策略定义:如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任务时,
ThreadPoolTaskExecutor
定义一些策略:Spring 通过
ThreadPoolExecutor.AbortPolicy
:抛出RejectedExecutionException
来拒绝新任务的处理。ThreadPoolExecutor.CallerRunsPolicy
:调用执行自己的线程运行任务,也就是直接在调用execute
方法的线程中运行(run
)被拒绝的任务,如果执行程序已关闭,则会丢弃该任务。因此这种策略会降低对于新任务提交速度,影响程序的整体性能。如果您的应用程序可以承受此延迟并且你要求任何一个任务请求都要被执行的话,你可以选择这个策略。ThreadPoolExecutor.DiscardPolicy
:不处理新任务,直接丢弃掉。ThreadPoolExecutor.DiscardOldestPolicy
:此策略将丢弃最早的未处理的任务请求。ThreadPoolTaskExecutor
或者我们直接通过ThreadPoolExecutor
的构造函数创建线程池的时候,当我们不指定RejectedExecutionHandler
饱和策略的话来配置线程池的时候默认使用的是ThreadPoolExecutor.AbortPolicy
。在默认情况下,ThreadPoolExecutor
将抛出RejectedExecutionException
来拒绝新来的任务 ,这代表你将丢失对这个任务的处理。 对于可伸缩的应用程序,建议使用ThreadPoolExecutor.CallerRunsPolicy
。当最大池被填满时,此策略为我们提供可伸缩队列(这个直接查看ThreadPoolExecutor
的构造函数源码就可以看出,比较简单的原因,这里就不贴代码了)。
3、 线程池的底层工作原理?
1、在创建了线程池后,等待提交过来的任务请求。
2、当调用 execute() 方法添加一个请求任务时,线程池会做如下判断:
1. 如果正在运行的线程数量小于corePoolSize,会马上创建线程运行这个任务
2. 如果正在运行的线程数量大于或等于corePoolSize,会将这个任务放入队列
3. 如果这时候队列满了且正在运行的线程数量还小于 maximumPoolSize,会创建非核心线程立刻运行这个任务
4. 如果队列满了且正在运行的线程数量大于或等于 maximumPoolSize,线程池会启用饱和策略来执行
3、当一个线程完成任务时,它会从队列中取下一个任务来执行
4、当一个线程无事可做超过一定的时间(keepAliveTime)时,会关闭线程
4、线程池的实际使用
《阿里巴巴 Java 开发手册》强制线程池不允许使用 Executors
去创建,而是通过 ThreadPoolExecutor
构造函数的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
不允许使用 Executors
去创建的原因:
1、FixedThreadPool 和 SingleThreadPool:
允许请求的工作队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
2、CachedThreadPool 和 ScheduledThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM