原文:https://blog.csdn.net/Lj_18210158431/article/details/81990535
jdk1.5引入Executor线程池框架,通过它把任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池,而不用关心该任务是如何执行、被哪个线程执行,以及什么时候执行。
1.初始化线程池(4种)
简介:
Java线程池的工厂类:Executors类,
初始化4种类型的线程池:
1.newFixedThreadPool()
说明:初始化一个指定线程数的线程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene作为阻塞队列
特点:即使当线程池没有可执行任务时,也不会释放线程。
使用场景:用于负载比较重的服务器,为了资源的合理利用,需要限制当前线程数量。
2.newCachedThreadPool()
说明:初始化一个可以缓存线程的线程池,默认缓存60s,线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
特点:在没有任务执行时,当线程的空闲时间超过keepAliveTime,会自动释放线程资源;当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
因此,使用时要注意控制并发的任务数,防止因创建大量的线程导致而降低性能。
使用场景:用于并发执行大量短期的小任务,或者是负载较轻的服务器。
3.newSingleThreadExecutor()
说明:初始化只有一个线程的线程池,内部使用LinkedBlockingQueue作为阻塞队列。
特点:如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行。
使用场景:用于串行执行任务的场景,每个任务必须按顺序执行,不需要并发执行。
4.newScheduledThreadPool()
特定:初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。
总结:除了newScheduledThreadPool的内部实现特殊一点之外,其它线程池内部都是基于ThreadPoolExecutor类(Executor的子类)实现的。
2.ThreadPoolExecutor内部具体实现:
(1)ThreadPoolExecutor类构造器语法形式:
ThreadPoolExecutor(corePoolSize,maxPoolSize,keepAliveTime,timeUnit,workQueue,threadFactory,handle); 方法参数:
corePoolSize:核心线程数
maxPoolSize:最大线程数
keepAliveTime:线程存活时间(在corePore<*<maxPoolSize情况下有用)
timeUnit:存活时间的时间单位
workQueue:阻塞队列(用来保存等待被执行的任务)
threadFactory:线程工厂,主要用来创建线程;
handler:表示当拒绝处理任务时的策略,有以下四种取值(饱和策略)
(2)注:关于workQueue参数的取值,JDK提供了4种阻塞队列
列类型供选择:
ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
InkedBlockingQuene:基于链表结构的阻塞队列,按FIFO排序任务,吞吐量通常要高于
SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直 于阻塞状态,吞吐量通常要高于ArrayBlockingQuene;
PriorityBlockingQuene:具有优先级的无界阻塞队列;
(3)handler:表示当拒绝处理任务时的策略,有以下四种取值(饱和策略)
问:最大线程数如果满了该如何处理?可使用饱和策略解决。
工作队列就有两种实现策略:无界队列和有界队列。无界队列不存在饱和的问题,但是其问题是当请求持续高负载的话,任务会无脑的加入工作队列,那么很可能导致内存等资源溢出或者耗尽。而有界队列不会带来高负载导致的内存耗尽的问题,但是有引发工作队列已满情况下,新提交的任务如何管理的难题,这就是线程池工作队列饱和策略要解决的问题。
饱和策略分为:Abort 策略, CallerRuns 策略,Discard策略,DiscardOlds策略。
注: 当线程池的饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,线程池提供了4种策略:
ThreadPoolExecutor.AbortPolicy:默认策略,新任务提交时直接抛出未检查的异常RejectedExecutionException异常。该异常可由调用者捕获。
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
ThreadPoolExecutor.DiscardPolicy:新提交的任务被抛弃,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(不适合工作队列为优先队列场景)
当然也可以根据应用场景实现RejectedExecutionHandler接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。
3.应用场景:
多线程最多的场景:;
多线程的常见应用场景:
1.web服务器本身;各种专用服务器(如游戏服务器)
2、后台任务,例如:定时向大量(100w以上)的用户发送邮件;
3、异步处理,例如:发微博、记录日志等;
4、分布式计算
4.总结:
线程池中的核心线程数,当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行;如果阻塞队列满了,那就创建新的线程执行当前任务;直到线程池中的线程数达到maxPoolSize,这时再有任务来,只能执行reject()处理该任务;
注:如果执行了线程池的prestartAllCoreThreads()方法,线程池会提前创建并启动所有核心线程。