java中常见的四种线程池
前言:
我们之前使用线程的时候都是使用new Thread来进行线程的创建,但是这样会有一些问题。如:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
而我们今天来学习和掌握另外一个新的技能,特别像一个线程池的一个接口类ExecutorService,下面我们来了解下java中Executors的线程池
Java通过Executors提供四种线程池,分别为:
newCachedThreadPool:创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool:创建一个定长的线程池,可控制线程的最大并发数,超出的线程在队列中等待。
看看Executors工厂内部是如何实现的
1 2 3 4 5 | public static ExecutorService newFixedThreadPool( int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } |
可以看到返回的是一个ThreadPoolExecutor对象,核心线程数和是最大线程数都是传入的参数,存活时间是0,时间单位是毫秒,阻塞队列是无界队列LinkedBlockingQueue。
由于队列采用的是无界队列LinkedBlockingQueue,最大线程数maximumPoolSize和keepAliveTime都是无效参数,拒绝策略也将无效,为什么?
这里又延伸出一个问题,无界队列说明任务没有上限,如果执行的任务比较耗时,那么新的任务会一直存放在线程池中,线程池的任务会越来越多
示例代码:
public class Main { public static void main(String[] args){ ExecutorService pool = Executors.newFixedThreadPool(4); for (int i = 0; i < 8; i++) { int finalI = i + 1; pool.submit(() -> { try { System.out.println("任务"+ finalI +":开始等待2秒,时间:"+LocalTime.now()+",当前线程名:"+Thread.currentThread().getName()); Thread.sleep(2000); System.out.println("任务"+ finalI +":结束等待2秒,时间:"+LocalTime.now()+",当前线程名:"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } }); } pool.shutdown(); } }
newCachedThreadPool方法也是返回ThreadPoolExecutor对象,核心线程是0,最大线程数是Integer的最MAX_VALUE,存活时间是60,时间单位是秒,SynchronousQueue队列。
从传入的参数可以得知,在newCachedThreadPool方法中的空闲线程存活时间时60秒,一旦超过60秒线程就会被终止。这边还隐含了一个问题,如果执行的线程较慢,而提交任务的速度快于线程执行的速度,那么就会不断的创建新的线程,从而导致cpu和内存的增长。
代码和newFixedThreadPool一样循环添加新的线程任务,我的电脑运行就会出现如下错误
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
看看Executors工厂内部是如何实现的
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
这里返回的是ScheduledThreadPoolExecutor对象,我们继续深入进去看看
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
这里调用的是父类的构造函数,ScheduledThreadPoolExecutor的父类是ThreadPoolExecutor,所以返回的也是ThreadPoolExecutor对象。核心线程数是传入的参数corePoolSize,线程最大值是Integer的MAX_VALUE,存活时间时0,时间单位是纳秒,队列是DelayedWorkQueue。
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService {}
下面是ScheduledExecutorService的一些方法
public interface ScheduledExecutorService extends ExecutorService {
//delay延迟时间,unit延迟单位,只执行1次,在经过delay延迟时间之后开始执行
public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);
public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);
//首次执行时间时然后在initialDelay之后,然后在initialDelay+period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,
long period,
TimeUnit unit);
//首次执行时间时然后在initialDelay之后,然后延迟delay时间执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
newSingleThreadExecutor :创建一个单线程化的线程池,它只会有唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
· Manus爆火,是硬核还是营销?