Java的线程池ThreadPoolExecutor及多种线程池实现详解
作者:好奇的7号
1、线程池状态含义
ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态,低 29 位表示线程数量,之所以将信息存储在一个变量中,是为了保证原子性。
具体的高三位与线程池状态如下,引用自网课的图片:
2、构造方法的参数、具体工作方式
public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
对于以上变量,其含义如下:corePoolSize 核心线程数目 (最多保留的线程数)
- maximumPoolSize 最大线程数目
- keepAliveTime 生存时间
- unit 生存时间的时间单位
- workQueue 阻塞队列
- threadFactory 线程工厂 - 可以为线程创建时起个好名字
- handler 拒绝策略
具体解释:
1.核心线程数:是指在线程池中始终保持存活的线程数量。在线程池中,当有新的任务到达时,线程池会创建新的线程来处理任务,但是当任务处理完毕后,线程并不会立即销毁,而是被放置在线程池中等待下一个任务的到来。 当将allowCoreThreadTimeout设置为 true 时,核心线程也会超时回收,像这样:
executor.allowCoreThreadTimeOut(true); // 允许回收核心线程
2.最大线程数目:指核心线程数+非核心线程数的总数。例如设置核心为5,最大为10,那么非核心(救急)则为10-5=5个
3.生存时间:线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将allowCoreThreadTimeout设置为 true 时,核心线程也会超时回收。
4.阻塞队列:如果核心线程都被占用没有空闲,此时又多来了新任务,则新来的任务会被加入阻塞队列阻塞等待。
5.拒绝策略:如果阻塞队列满了,继续来任务,那么就创建救急线程来执行新任务,但如果救急线程也不够(达到最大线程数),再来任务,因为线程池已经填满了到极限了,所以就要拒绝新来的任务了。jdk和各种框架有多种拒绝策略的实现:
- AbortPolicy 让调用者抛出 RejectedExecutionException 异常,这是默认策略
- CallerRunsPolicy 让调用者运行任务
- DiscardPolicy 放弃本次任务
- DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之
- ActiveMQ 的实现,带超时等待(60s)尝试放入队列(较好)
3、线程池有哪些常用的实现方式
3.1 newFixedThreadPool
全是、多个核心线程不回收
fixed,即“固定的”,线程数固定,能够控制线程的最大并发数:
(1)源码中,其核心线程数和最大线程数都是nThreads,即相等,说明没有非核心线程
(2)阻塞队列是无界的,可以放任意数量的任务。
(3)核心线程不会自动回收,直到被明确打断:“The threads in the pool will exist until it is explicitly shutdown.”
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
//具体使用示例 //1.创建定长线程池对象,线程数量固定为3 ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); //2. 创建Runnable线程对象以及执行的任务 Runnable task =new Runnable(){ public void run() { System.out.println("执行任务啦"); } }; //3. 向线程池提交任务 fixedThreadPool.execute(task);
3.2newCachedThreadPool
全是非核心,定时回收
Cached,缓存,是说这种线程池的实现像缓存一样是可变的:
(1)核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,线程的空闲生存时间是 60s,意味着:这种线程池内都是非核心线程、可以无限创建、定时60s回收。
(2)队列采用了 SynchronousQueue ,特点是,它没有容量,没有线程来取是放不进去的。
(3)适合场景:执行大量、耗时少的任务。
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
3.3newSingleThreadExecutor
就一个核心线程,不回收
Single,即单线程的线程池:只有 1 个核心线程,无非核心线程,执行后不会立即回收。 这样相当于顺序执行,不需要处理线程同步问题。
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
3.4newScheduledThreadPool
核心非核心都有,非核心定时回收
Scheduled即定时的:核心线程数量固定,非核心线程数量无限多但会定时回收,当非核心线程执行完闲置 10ms 后则回收,任务队列为延时阻塞队列。 应用场景:执行定时或周期性的任务。
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
到此这篇关于Java的线程池ThreadPoolExecutor及多种线程池实现详解的文章就介绍到这了,更多相关Java线程池ThreadPoolExecutor内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!