场景:mysql有100w条数据,要根据id更新到Elasticsearch。
处理方式:100w 条数据分页查询,每次查询1w条数据,再将这1w条数据分成10个分片,每个分片1000条数据,每个分片的1000数据由一条线程去执行处理,相当于10个分片由10个线程处理。
线程池工具类
/**
* 线程池工具类
*/
public class ThreadUtil {
/**
* 线程池实例
*/
private static ThreadPoolExecutor executor = null;
/**
* 核心线程数
*/
private static final Integer THREADPOOL_COREPOOLSIZE = 30;
/**
* 最大线程数
*/
private static final Integer THREADPOOL_MAXPOOLSIZE = 200;
/**
* 线程等待回收的存活时间,单位:分钟
*/
private static final long THREADPOOL_KEEPALIVETIME = 10;
/**
* 初始化线程池
* 线程池拒绝策略为默认的拒绝策略,如果不能加入工作队列就抛出RejectedExecutionException异常
*/
static {
executor = new ThreadPoolExecutor(THREADPOOL_COREPOOLSIZE,
THREADPOOL_MAXPOOLSIZE,
THREADPOOL_KEEPALIVETIME,
TimeUnit.MINUTES,
new ArrayBlockingQueue<>(80),
new DefaultThreadFactory("defaultPool"),
new ThreadPoolExecutor.AbortPolicy());
}
/**
* 使用线程池运行任务,线程无返回值
* @param task
*/
public static void execute(Runnable task){
executor.execute(task);
}
/**
* 使用线程池提交异步任务,任务运行带返回值
* @param task 实现了Callable接口的线程
* @return
*/
public static Future submit(Callable task){
return executor.submit(task);
}
/**
* 停止线程池
*/
public static void shutdown() {
executor.shutdown();
}
public static ThreadPoolExecutor getExecutor(){
return executor;
}
}
模拟测试数据
public class ListFactory {
private List<String> data;
private int curPage = 0;
private int pageSize = 20;
public ListFactory() {
String[] arr = new String[]{"9", "1", "2", "1", "3", "5", "8", "3", "9", "6", "8", "7", "5", "5", "1", "1", "5", "4", "9", "4", "2", "7", "8", "8", "5", "1", "4", "3", "8", "1", "2", "4", "2", "4", "4", "8", "8", "4", "4", "5", "3", "3", "6", "4", "7", "6", "5", "4", "1", "8", "4", "3", "6", "7", "7", "4", "5", "3", "9", "9", "5", "5", "6", "1", "4", "1", "9", "3", "5", "1", "1", "7", "9", "4", "9", "2", "8", "7", "6", "6", "4", "1", "6", "6", "9", "1", "5", "2", "1", "1", "8", "1", "6", "3", "1", "8", "4"};
this.data = Arrays.asList(arr);
System.out.println("全部数据 " + this.data);
}
public List<String> getNext() {
int start = curPage * pageSize;
//数据取完了
if (start > data.size()) {
return null;
}
int end = Math.min((start + pageSize), data.size());
//下一页
curPage++;
return this.data.subList(start, end);
}
public int getSize() {
return this.data.size();
}
public int getCurPage() {
return this.curPage;
}
public static void main(String[] args) {
ListFactory listFactory = new ListFactory();
for (int i = 0; i < 10; i++) {
List<String> data = listFactory.getNext();
System.out.println(data);
}
}
}
执行的处理任务
public class MKTask implements Runnable {
private final List<String> items;
private int cur = 0;
public MKTask(List<String> items) {
this.items = items;
}
@Override
public void run() {
for (int j = 0; j < items.size(); j++) {
try {
//模拟业务处理耗时
int s = 1 + (int) (Math.random() * 3);
TimeUnit.SECONDS.sleep(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.cur = j;
}
}
public int getCur() {
return this.cur + 1;
}
}
主线程分片
public class MultiThread {
List<MKTask> taskList = new ArrayList<>();
/**
* 将任务切分,由多线程处理,并打印处理进度
*/
@Test
public void handleAll() throws InterruptedException {
ListFactory listFactory = new ListFactory();
int total = listFactory.getSize();
while (true) {
//获取翻页数据
List<String> data = listFactory.getNext();
//模拟取数据耗时
TimeUnit.SECONDS.sleep(1);
if (data == null) {
break;
}
//每个线程处理 5 条
int threadCount = 5;
System.out.println("当前页" + listFactory.getCurPage());
handlePageDate(data, threadCount);
}
//计算任务进度
while (true) {
TimeUnit.SECONDS.sleep(2);
//每2秒打印一次各个线程的处理进度
int sum = 0;
for (MKTask mkTask : taskList) {
int cur = mkTask.getCur();
sum = sum + cur;
}
DecimalFormat dF = new DecimalFormat("0.00");
float f = (float) sum / total;
if (f == 1.0) {
System.out.println("全部任务完成");
break;
}
System.out.println("任务处理进度 " + dF.format(f * 100) + "%");
}
}
public void handlePageDate(List<String> data, int threadCount) {
int pageSize = data.size();
//分成 segment 份,需要 segment 个线程处理
int segment = (int) Math.ceil((double) pageSize / threadCount);
System.out.println("当前页数据 " + data + " 一共有 " + pageSize + " 条, 每个线程处理 " + threadCount + " 条数据, 需要 " + segment + " 个线程处理");
for (int i = 0; i < segment; i++) {
int start = i * threadCount;
int end = (i == segment - 1) ? pageSize : (start + threadCount);
List<String> subList = data.subList(start, end);
MKTask mkTask = new MKTask(subList);
taskList.add(mkTask);
System.out.println(start + " - " + end + " 范围数据 " + subList + " 交给一条线程处理");
ThreadUtil.execute(mkTask);
}
}
}
执行结果
全部数据 [9, 1, 2, 1, 3, 5, 8, 3, 9, 6, 8, 7, 5, 5, 1, 1, 5, 4, 9, 4, 2, 7, 8, 8, 5, 1, 4, 3, 8, 1, 2, 4, 2, 4, 4, 8, 8, 4, 4, 5, 3, 3, 6, 4, 7, 6, 5, 4, 1, 8, 4, 3, 6, 7, 7, 4, 5, 3, 9, 9, 5, 5, 6, 1, 4, 1, 9, 3, 5, 1, 1, 7, 9, 4, 9, 2, 8, 7, 6, 6, 4, 1, 6, 6, 9, 1, 5, 2, 1, 1, 8, 1, 6, 3, 1, 8, 4]
当前页1
当前页数据 [9, 1, 2, 1, 3, 5, 8, 3, 9, 6, 8, 7, 5, 5, 1, 1, 5, 4, 9, 4] 一共有 20 条, 每个线程处理 5 条数据, 需要 4 个线程处理
0 - 5 范围数据 [9, 1, 2, 1, 3] 交给一条线程处理
5 - 10 范围数据 [5, 8, 3, 9, 6] 交给一条线程处理
10 - 15 范围数据 [8, 7, 5, 5, 1] 交给一条线程处理
15 - 20 范围数据 [1, 5, 4, 9, 4] 交给一条线程处理
当前页2
当前页数据 [2, 7, 8, 8, 5, 1, 4, 3, 8, 1, 2, 4, 2, 4, 4, 8, 8, 4, 4, 5] 一共有 20 条, 每个线程处理 5 条数据, 需要 4 个线程处理
0 - 5 范围数据 [2, 7, 8, 8, 5] 交给一条线程处理
5 - 10 范围数据 [1, 4, 3, 8, 1] 交给一条线程处理
10 - 15 范围数据 [2, 4, 2, 4, 4] 交给一条线程处理
15 - 20 范围数据 [8, 8, 4, 4, 5] 交给一条线程处理
当前页3
当前页数据 [3, 3, 6, 4, 7, 6, 5, 4, 1, 8, 4, 3, 6, 7, 7, 4, 5, 3, 9, 9] 一共有 20 条, 每个线程处理 5 条数据, 需要 4 个线程处理
0 - 5 范围数据 [3, 3, 6, 4, 7] 交给一条线程处理
5 - 10 范围数据 [6, 5, 4, 1, 8] 交给一条线程处理
10 - 15 范围数据 [4, 3, 6, 7, 7] 交给一条线程处理
15 - 20 范围数据 [4, 5, 3, 9, 9] 交给一条线程处理
当前页4
当前页数据 [5, 5, 6, 1, 4, 1, 9, 3, 5, 1, 1, 7, 9, 4, 9, 2, 8, 7, 6, 6] 一共有 20 条, 每个线程处理 5 条数据, 需要 4 个线程处理
0 - 5 范围数据 [5, 5, 6, 1, 4] 交给一条线程处理
5 - 10 范围数据 [1, 9, 3, 5, 1] 交给一条线程处理
10 - 15 范围数据 [1, 7, 9, 4, 9] 交给一条线程处理
15 - 20 范围数据 [2, 8, 7, 6, 6] 交给一条线程处理
当前页5
当前页数据 [4, 1, 6, 6, 9, 1, 5, 2, 1, 1, 8, 1, 6, 3, 1, 8, 4] 一共有 17 条, 每个线程处理 5 条数据, 需要 4 个线程处理
0 - 5 范围数据 [4, 1, 6, 6, 9] 交给一条线程处理
5 - 10 范围数据 [1, 5, 2, 1, 1] 交给一条线程处理
10 - 15 范围数据 [8, 1, 6, 3, 1] 交给一条线程处理
15 - 17 范围数据 [8, 4] 交给一条线程处理
任务处理进度 48.45%
任务处理进度 70.10%
任务处理进度 87.63%
任务处理进度 96.91%
全部任务完成
本文介绍了如何使用Java实现数据分片和多线程处理的场景。通过线程池工具类,模拟100w条数据并分页查询,每次处理1w条数据,进一步分为10个分片,每个分片由单独线程执行,提高数据导入Elasticsearch的效率。

1085

被折叠的 条评论
为什么被折叠?



