* corePoolSize: 线程池维护线程的最少数量 * maximumPoolSize:线程池维护线程的最大数量 * keepAliveTime:非核心线程最大存活时长, 线程池维护线程所允许的空闲时间 * unit: 线程池维护线程所允许的空闲时间的单位 * workQueue: 线程池所使用的缓冲队列 * handler: 线程池对拒绝任务的处理策略 // 五个参数的构造函数 public ThreadPoolExecutor(int corePoolSize,//核心线程数 int maximumPoolSize,//最大线程数 long keepAliveTime,//非核心线程最大存活时长 TimeUnit unit,//时长单位 BlockingQueue<Runnable> workQueue)//阻塞队列,存放着等待执行的线程任务 // 六个参数的构造函数-1 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)//创建线程的工厂 // 六个参数的构造函数-2 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)//拒绝策略,当线程池无法再接受任务时调用 // 七个参数的构造函数 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
必传入参
corePoolSize:指定了线程池中的核心线程数,即不会因线程空闲而被销毁的线程。线程池初始化时默认是没有线程的,当任务来临时才开始创建线程去执行任务。它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue任务队列中去。
核心线程:线程池中有两类线程,核心线程和非核心线程。核心线程默认情况下会一直存在于线程池中,即使这个核心线程什么都不干(铁饭碗),而非核心线程如果长时间的闲置,就会被销毁(临时工)。
maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量。
该值等于核心线程数量 + 非核心线程数量。
最大线程数,在核心线程数的基础上可能会额外增加一些非核心线程,需要注意的是只有当workQueue队列填满时才会创建多于corePoolSize的线程(线程池总线程数不超过maxPoolSize)。
keepAliveTime:非核心线程最大存活时长。
非核心线程如果处于闲置状态超过该值,就会被销毁。如果设置allowCoreThreadTimeOut(true),则会也作用于核心线程。
非核心线程的空闲时间超过keepAliveTime就会被自动终止回收掉,注意当corePoolSize=maxPoolSize时,keepAliveTime参数也就不起作用了(因为不存在非核心线程);非核心线程:当线程池中空闲线程数量超过corePoolSize时,多余的线程就叫非核心线程。
unit:keepAliveTime的单位。
TimeUnit是一个枚举类型 ,包括以下属性:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000 MICROSECONDS : 1微秒 = 1毫秒 / 1000 MILLISECONDS : 1毫秒 = 1秒 /1000 SECONDS : 秒 MINUTES : 分 HOURS : 小时 DAYS : 天
workQueue:阻塞队列,存放着等待执行的Runnable任务对象。它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列、延迟队列几种。
SynchronousQueue:同步队列,内部容量为0,每个put操作必须等待一个take操作,反之亦然。
LinkedBlockingQueue:链式阻塞队列,底层数据结构是链表,默认大小是Integer.MAX_VALUE,也可以指定大小。
ArrayBlockingQueue:数组阻塞队列,底层数据结构是数组,需要指定队列的大小。
PriorityBlockingQueue:基于优先级的无界阻塞队列(优先级的判断通过构造函数传入的Compator对象来决定),内部控制线程同步的锁采用的是非公平锁。
DelayQueue:延迟队列,该队列中的元素只有当其指定的延迟时间到了,才能够从队列中获取到该元素
选传入参
ThreadPoolExecutor.AbortPolicy:默认拒绝处理策略,丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃新来的任务,但是不抛出异常.
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列头部(最旧的)的任务,然后重新尝试执行程序(如果再次失败,重复此过程)。
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。
1、线程池数量设置
io密集型程序:线程数=cpu核数+1;(计算密集型,但是任务越多,任务切换时间越多,则执行效率低,配置线程数尽可能小,则同时进行的任务数量等于cpu核数,线程数=cpu核数+1,处理任务中尽量少打日志,较少IO操作)
cpu密集型程序:2*CPU核数;(涉及网络和磁盘IO操作,IO密集型任务线程并不是一直在执行任务,配置线程数尽可能多,如2*CPU核数)
2、线程池的处理流程
一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法
当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、如果此时线程池中的数量<corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、如果此时线程池中的数量= corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量>corePoolSize,缓冲队列workQueue满,并且线程池中的数量<maximumPoolSize,建新的线程来处理被添加的任务
4、如果此时线程池中的数量>corePoolSize,缓冲队列workQueue满,并且线程池中的数量=maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
newCachedThreadPool
核心线程数为0,非核心线程数为Integer.MAX_VALUE,所以这是一个线程只要空闲60秒就会被回收的线程池,适用于短时间高并发的处理业务,而在峰值过后并不会占用系统资源。
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), threadFactory); }
newFixedThreadPool
核心线程数量和总线程数量相等,都是传入的参数nThreads,这是一个池中都是核心线程的线程池,所有线程都不会销毁。执行任务的全是核心线程,当没有空闲的核心线程时,任务会进入到阻塞队列,直到有空闲的核心线程才会去从阻塞队列中取出任务并执行,也导致该线程池基本不会发生使用拒绝策略拒绝任务。还有因为LinkedBlockingQueue阻塞队列的大小默认是Integer.MAX_VALUE,如果使用不当,很可能导致内存溢出
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
newSingleThreadExecutor
有且仅有一个核心线程的线程池( corePoolSize == maximumPoolSize=1),使用了LinkedBlockingQueue(容量很大),所以,不会创建非核心线程。所有任务按照先来先执行的顺序执行。如果这个唯一的线程不空闲,那么新来的任务会存储在任务队列里等待执行。
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), threadFactory)); }
newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行,这是一个支持延时任务执行的线程池。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); } public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, //注意这里使用延迟队列 new DelayedWorkQueue()); }
使用案例
ScheduledExecutorService pool = Executors.newScheduledThreadPool(10); // 入参Runnable实例,延时时间,时间单位 pool.schedule(() -> System.out.println("执行1"), 5, TimeUnit.SECONDS); pool.schedule(() -> System.out.println("执行2"), 4, TimeUnit.SECONDS); pool.schedule(() -> System.out.println("执行3"), 6, TimeUnit.SECONDS); 打印结果: 执行2 执行1 执行3
corePoolSize == maximumPoolSize
,所以FixedThreadPool
只会创建核心线程。 而CachedThreadPool
因为corePoolSize=0
,所以只会创建非核心线程。FixedThreadPool
在getTask()
方法,如果队列里没有任务可取,线程会一直阻塞在LinkedBlockingQueue.take()
,线程不会被回收。CachedThreadPool
会在60s后收回。FixedThreadPool
占用资源更多。FixedThreadPool
是因为阻塞队列可以很大(最大为Integer.MAX_VALUE
),故几乎不会触发拒绝策略;CachedThreadPool
是因为线程池很大(最大为Integer.MAX_VALUE
),几乎不会导致线程数量大于最大线程数,故几乎不会触发拒绝策略。参考文档:
https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
https://blog.csdn.net/qq_41135605/article/details/11445310