问题
在我开发中遇到一个list大集合,list数据太大,通过业务层面已经做了一些优化处理,但是还未达标客户要求(要求60s内完成解析),所以只能采用线程去处理,也看了一些博主的文章,受此启发,自己也发个文章记录下。
创建线程池
我是使用的jdk自带的线程池,ThreadPoolExecutor来创建线程池
this.executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
并且配置相关的参数:线程核心数、最大线程数、空闲时间、单位、队列
/**
* 最大线程数量 使用cpu核心数
*/
private static final int maximumPoolSize = Runtime.getRuntime().availableProcessors();
/**
* 核心线程数
*/
private static final int corePoolSize = maximumPoolSize / 2;
/**
* 空闲时间
*/
private static final long keepAliveTime = 60;
/**
* 单位 秒
*/
private static final TimeUnit unit = TimeUnit.SECONDS;
/**
* 任务队列数量
*/
private static final BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1000);
// 线程池实例
private final ExecutorService executorService;
然后使用AtomicReference来存储实例保证线程安全
private static final AtomicReference<ThreadPoolUtil> INSTANCE = new AtomicReference<>();
然后使用单例模式来获取线程池实例,这里使用的是双重加锁的模式来实现单例
// 双重检查锁定模式获取实例
public static ThreadPoolUtil getInstance() {
ThreadPoolUtil instance = INSTANCE.get();
if (instance == null) {
synchronized (ThreadPoolUtil.class) {
instance = INSTANCE.get();
if (instance == null) {
instance = new ThreadPoolUtil();
INSTANCE.set(instance);
}
}
}
return instance;
}
能够让我们每次获取的时候都能获取同一个线程池实例对象,不会去重复创建线程池。然后向外提供线程池提交任务的方法,可以通过Future来获取返回内容
// 带返回值 提交任务到线程池
public Future<T> submitTask(Callable task) {
return executorService.submit(task);
}
完整代码如下:
package com.example.demo.utils;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Aescription: 单例线程池
* @Author: huao
* @Date: 2024/8/29 15:10
*/
@Slf4j
public class ThreadPoolUtil<T> {
// 使用AtomicReference来存储实例,确保线程安全
private static final AtomicReference<ThreadPoolUtil> INSTANCE = new AtomicReference<>();
/**
* 最大线程数量 使用cpu核心数
*/
private static final int maximumPoolSize = Runtime.getRuntime().availableProcessors();
/**
* 核心线程数
*/
private static final int corePoolSize = maximumPoolSize / 2;
/**
* 空闲时间
*/
private static final long keepAliveTime = 60;
/**
* 单位 秒
*/
private static final TimeUnit unit = TimeUnit.SECONDS;
/**
* 任务队列数量
*/
private static final BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(1000);
// 线程池实例
private final ExecutorService executorService;
// 私有构造函数
private ThreadPoolUtil() {
log.info("线程池参数:核心线程数{},最大线程数{},空闲时间{}s,队列大小{}",corePoolSize,maximumPoolSize,keepAliveTime,workQueue.size());
this.executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
// 双重检查锁定模式获取实例
public static ThreadPoolUtil getInstance() {
ThreadPoolUtil instance = INSTANCE.get();
if (instance == null) {
synchronized (ThreadPoolUtil.class) {
instance = INSTANCE.get();
if (instance == null) {
instance = new ThreadPoolUtil();
INSTANCE.set(instance);
}
}
}
return instance;
}
// 带返回值 提交任务到线程池
public Future<T> submitTask(Callable task) {
log.info("队列任务数量:{}",workQueue.size());
return executorService.submit(task);
}
// 不带返回值 提交任务到线程池
public void submitTask(Runnable task){
executorService.submit(task);
}
// 关闭线程池
public void shutdown() {
executorService.shutdown();
}
// 尝试优雅关闭线程池,等待已提交的任务完成
public void shutdownNow() {
executorService.shutdownNow();
}
}
代码测试
测试内容代码如下
package com.example.demo.utils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
public class Test {
public static void main(String[] args) {
//初始化60个数据
List<String> list = new ArrayList<>();
int size = 60;
for (int i = 0; i < size; i++) {
list.add("task-"+i);
}
//处理完成后的结果集
List<String> results = new ArrayList<>();
long millis = System.currentTimeMillis();
System.out.println("=========不使用多线程处理数据============");
System.out.println("===============开始解析=================");
results.addAll(execute(list));
System.out.println("解析完成,解析数据量:"+results.size()+",耗时:"+(System.currentTimeMillis() - millis) / 1000 + "秒");
results.clear();
System.out.println("===============分割线===================");
System.out.println("===========使用多线程处理数据============");
System.out.println("===============开始解析=================");
long millis1 = System.currentTimeMillis();
try {
ThreadPoolUtil<List<String>> threadPoolUtil = ThreadPoolUtil.getInstance();
List<Future<List<String>>> futureList = new ArrayList<>();
//这里根据线程池的核心数去进行分段处理,最好不要一开始就达到最大线程数,只需要一半即可
int numThreads = Runtime.getRuntime().availableProcessors() / 2;
for (int i = 0; i < list.size(); i += list.size() / numThreads) {
int start = i;
int end = Math.min(i + list.size() / numThreads, list.size());
List<String> subList = list.subList(start, end);
Callable<List<String>> task = () -> {
return execute(subList);
};
Future<List<String>> future = threadPoolUtil.submitTask(task);
futureList.add(future);
}
for (Future<List<String>> future : futureList) {
results.addAll(future.get());
}
}catch (Exception e){
e.printStackTrace();
}
System.out.println("解析完成,解析数据量:"+results.size()+",耗时:"+(System.currentTimeMillis() - millis1) / 1000 + "秒");
}
private static List<String> execute(List<String> subList){
List<String> result = new ArrayList<>();
for (String s : subList) {
try {
//模拟耗时每次1s
Thread.sleep(1000);
result.add(s + Thread.currentThread().getName());
}catch (Exception e){
e.printStackTrace();
}
}
return result;
}
}
我是初始化60条数据,每条数据处理模拟要耗时1秒,不使用线程的情况下就需要执行60秒,使用线程后可以优化到11秒。运行结果如下:
=========不使用多线程处理数据============
===============开始解析=================
解析完成,解析数据量:60,耗时:60秒
===============分割线===================
===========使用多线程处理数据============
===============开始解析=================
15:52:36.640 [main] INFO com.example.demo.utils.ThreadPoolUtil - 线程池参数:核心线程数8,最大线程数16,空闲时间60s,队列大小0
15:52:36.665 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
15:52:36.666 [main] INFO com.example.demo.utils.ThreadPoolUtil - 队列任务数量:0
解析完成,解析数据量:60,耗时:11秒
有什么问题,欢迎在评论区留言。