一、线程池
提供了一个线程对列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高响应的速度。
二、线程池的体系结构:
java.util.concurrent.Executor:负责线程的使用与调度的根接口
ExecutorService 子接口:线程池的主要接口
ThreadPoolExecutor 线程池的实现类
ScheduledExecutorService 子接口
ScheduledThreadPoolExecutor:继承ThreadPoolExecutor,实现ScheduledExecutorService
三、工具类:Executors
ExecutorService Executors.newFixedThreadPool :创建固定大小的线程池
ExecutorService Executors.newCachedThreadPool():缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
ExecutorService Executors.newSingleThreadExecutor():创建单个线程池。线程池中只有一个线程
ScheduledExecutorService Executors.newSingleThreadScheduledExecutor() :创建固定大小的线程,可以延迟或定时的执行任务
四、使用ThreadPoolExecutor自定义创建多线程
ThreadPoolExecutor构造函数
(1)5个基本参数
corePoolSize: 核心池的大小, 或者说线程池维护线程的最少数量 (core : 核心)
maximumPoolSize: 线程池中线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
代码语言:javascript复制public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
(1)2个可选参数
handler: 线程池对拒绝任务的处理策略
threadFactory:自定义线程工厂,可以使用om.google.guava ThreadFactoryBuilder
指定线程名称
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("thread-%d")
.build();
eg:
代码语言:javascript复制ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
4,
4,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<>(10),
threadFactory,// 线程工厂可以不用传参
new ThreadPoolExecutor.CallerRunsPolicy()//线程拒绝策略,也可以不用传参
);
五:线程池ThreadPoolExecutor中execute和submit方法对比
(1)定义方法的类不同
submit是在ExecutorService接口中定义的,而execute方法是在Executor类中定义的,ThreadPoolExecutor实现。
execute:
代码语言:javascript复制public interface Executor {
void execute(Runnable command);
}
submit
// ExecutorService接口中定义的方法:
代码语言:javascript复制Future submit(Runnable task);
Future submit(Runnable task, T result);
Future submit(Callable task);
(2)返回值类型不同
execute方法返回值为空,submit方法会以Future的形式返回线程的执行结果。
(3)对异常的处理方式不同
如果执行的任务中产生了异常,execute()方法会直接打印产生的异常的堆栈,由于该异常是在子线程中产生,主线程中包围在execute方法周围的try-catch语句不能捕获异常。
而submit提交的子线程如果产生了异常,当调用submit方法返回的Future实例的get方法时,可以在主线程try-catch捕获异常。如果不调用get方法就不能捕获异常
项目应用(使用场景):
1)并发量大的场景,批量查询,提高。多线程(线程池)分配处理
代码语言:javascript复制//List分批处理
List> productList = Lists.partition(activityProductBatchSaveDTO.getProductIds(), 15);
long time = System.currentTimeMillis();
//
Map productFullsById = new ConcurrentHashMap<>();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(productList.size(),productList.size(),1, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100000),
new ThreadFactoryBuilder().setNameFormat("OMG-Tool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
CountDownLatch countDownLatch =new CountDownLatch(productList.size());
productList.forEach(productIds->{
threadPoolExecutor.execute(()->{
ProductIdRequest request = BeanMapper.map(req, ProductIdRequest.class);
request.setProductIds(productIds);
productFullsById.putAll(productFacade.getProductsByIdsV3(request));
countDownLatch.countDown();
});
});
try {
countDownLatch.await();
}catch (Exception e){
}finally {
threadPoolExecutor.shutdown();
}
2)耗时较长的任务。比如用户注册之后发送邮件、短信之类的操作,这种任务就算失败了影响也不大,可以使用异步线程。
3)定时任务,比如定期更新配置文件、备份数据之类的任务。
总结
利用线程池,可以动态配置线程池相关参数,包括核心线程池,非核心线程池,当内存紧张的时候,就可以释放部分线程池,或者直接关闭非核心线程池,并具有如下优点:
(1)降低资源销毁。重用存在的线程,减少对象创建销毁的开销
(2)提高线程管理性。可有效的控制最大并发线程数、提高系统资源的使用率,同时避免过多资源竞争,避免阻塞
(3)提高响应速度,任务到达可以不用创建线程就可以执行。提供定时执行、定期执行、单线程、并发数控制等功能
我正在参与2023腾讯技术创作特训营第四期有奖征文,快来和我瓜分大奖!