java线程池相关问题
线程池参数
- corePoolSize 核心线程池大小----10
- maximumPoolSize 最大线程池大小----30
- keepAliveTime 线程池中超过corePoolSize数目的空闲线程最大存活时间----30+
- 单位TimeUnit TimeUnit keepAliveTime时间单位----TimeUnit.MINUTES
- workQueue 阻塞队列----new ArrayBlockingQueue(10)====10容量的阻塞队列 ,LinkedBlockingQueue:最大容量是Integer.MAX_VALUE
- threadFactory 新建线程工厂----new CustomThreadFactory()====定制的线程工厂
- rejectedExecutionHandler 当提交任务数超过maxmumPoolSize+workQueue之和时, 即当提交第41个任务时(前面线程都没有执行完,此测试方法中用sleep(100)), 任务会交给RejectedExecutionHandler来处理
线程池线程提交策略
- 当线程池小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
- 当线程池达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行
- 当workQueue已满,且maximumPoolSize>corePoolSize时,新提交任务会创建新线程执行任务
- 当提交任务数超过maximumPoolSize时,新提交任务,使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy
- 当线程池中超过corePoolSize线程,空闲时间达到keepAliveTime时,关闭空闲线程
- 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize线程空闲时间达到keepAliveTime也将关闭
线程池的拒绝策略
当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
-
ThreadPoolExecutor.AbortPolicy : 丢弃任务并抛出RejectedExecutionException异常。 默认策略
-
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
-
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
-
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
几种常用线程池,以及可能发生的问题:
SingleThreadPool 介绍
ExecutorService scheduledThreadPool = Executors.newSingleThreadExecutor();
//SingleThreadExecutor实现方式
new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
单个线程的线程池,使用唯一的工作线程来执行任务。保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
FixedThreadPool介绍
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
//FixedThreadPool实现方式
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
- 定长线程池,corePoolSize=maximumPoolSize
- 可指定线程池大小,可控制线程最大并发数
- 超出的线程会被提交到工作队列中(LinkedBlockedQueue 最大长度Integer.MAX_VALUE)
SingleThreadPool 和FixedThreadPool可能出现的问题
线程数目固定,满了之后再提交,允许请求的最大线程数为,可能会堆积大量请求导致OOM
CachedThreadPool 介绍
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//newCachedThreadPool实现方式
new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
- 可缓存线程池,自动回收空闲线程
- 当线程池中的线程空闲时间超过60s,便会终止该空闲线程并从缓存线程池中移除
- 超出maximumPoolSize的线程会创建新的线程
ScheduledThreadPool介绍
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
//scheduledThreadPool 实现方式
new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
- 大小无限制的线程池,支持定时和周期性的执行线程
- 最大线程数Integer.MAX_VALUE
CachedThreadPool 和 scheduledThreadPool
会直接创建新的线程,最大线程数为Integer.Max_value,可能会创建大量的线程从而导致OOM
使用线程池的建议
如何设置线程池大小?
- 高并发、任务执行时间较短的业务:线程数设置为CPU核数+1
- 低并发、IO操作密集的业务:线程数可设置的较大些
- 低并发、计算型的业务:线程数设置为CPU核数+1
- 高并发、任务时间长的业务:可利用缓存或者中间件来对任务进行拆分或解耦
使用线程池的好处
- 减少了创建和销毁线程的时间
- 控制线程的最大并发数