多线程分批处理数据

在工作中经常会遇到一个业务操作要处理成千上万的数据,耗时非常严重,性能严重不达标,这个时候就会想到采用多线程来执行业务来提高执行效率,在改背景下本人写了一个可以直接套用业务的demo,在此记录,以备随时用到来copy代码

首先定义个执行业务任务的接口

package AtomicTest;

/**
 * desc:
 * author:wangqiangac
 * date:2023/12/21 14:48
 */
public interface MyTask {

    <T> String  work(T www) throws Exception;
}

再创建一个构建线程池的工具类,代码如下,线程池参数可以根据自己需要做成可配置

package AtomicTest;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * desc:
 * author:wangqiangac
 * date:2023/12/21 17:17
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ThreadPoolBuilder {
    int corePoolSize;//设置ThreadPoolExecutor的核心池大小。默认值为1。此设置可以在运行时进行修改,例如通过JMX进行修改
    int maxPoolSize;//设置ThreadPoolExecutor的最大池大小。默认值为Integer。最大值。此设置可以在运行时进行修改,例如通过JMX进行修改。
    int queueCapacity;//设置ThreadPoolExecutor的BlockingQueue的容量。默认值为Integer。最大值
    int keepAliveSeconds;//设置ThreadPoolExecutor的保持活动秒数。默认值为60。
    String threadNamePrefix;//自定义名称前缀
    boolean prestartAllCoreThreads;//指定是否启动所有核心线程,使它们空闲等待工作。
    RejectedExecutionHandler rejectedExecutionHandler;
    public ThreadPoolBuilder setCorePoolSize(int corePoolSize) {
        this.corePoolSize = corePoolSize;
        return this;
    }

    public ThreadPoolBuilder setMaxPoolSize(int maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
        return this;
    }

    public ThreadPoolBuilder setQueueCapacity(int queueCapacity) {
        this.queueCapacity = queueCapacity;
        return this;
    }

    public ThreadPoolBuilder setKeepAliveSeconds(int keepAliveSeconds) {
        this.keepAliveSeconds = keepAliveSeconds;
        return this;
    }

    public ThreadPoolBuilder setThreadNamePrefix(String threadNamePrefix) {
        this.threadNamePrefix = threadNamePrefix;
        return this;
    }

    public ThreadPoolBuilder setPrestartAllCoreThreads(boolean prestartAllCoreThreads) {
        this.prestartAllCoreThreads = prestartAllCoreThreads;
        return this;
    }

    public ThreadPoolBuilder setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) {
        this.rejectedExecutionHandler = rejectedExecutionHandler;
        return this;
    }

    public ThreadPoolExecutor builderThreadPoolExecutor(){
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
        threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
        threadPoolTaskExecutor.setQueueCapacity(queueCapacity);
        threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveSeconds);
        threadPoolTaskExecutor.setThreadNamePrefix(threadNamePrefix);
        threadPoolTaskExecutor.setPrestartAllCoreThreads(prestartAllCoreThreads);
        threadPoolTaskExecutor.setRejectedExecutionHandler(rejectedExecutionHandler);
        threadPoolTaskExecutor.initialize();
        ThreadPoolExecutor threadPoolExecutor = threadPoolTaskExecutor.getThreadPoolExecutor();
        return threadPoolExecutor;
    }
}

然后创建一个用于执行批量计算的工具类,如下

package AtomicTest;

import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * desc:
 * author:wangqiangac
 * date:2023/12/22 15:56
 */
@Slf4j
public class ThreadUtil {
    /**
     * 按照批次处理任务
     * @param threadPoolExecutor
     * @param list
     * @param batchCount
     */
    public static <T> List<List<T>> bathCalc(ThreadPoolExecutor threadPoolExecutor, List<T> list,int batchCount,String taskName,MyTask myTask) throws InterruptedException, ExecutionException {
        List<Callable<Object>> callables = new ArrayList<>();
        List<List<T>> partList = IntStream.range(0, list.size())
                .boxed()
                .collect(Collectors.groupingBy(index -> index / batchCount))
                .values()
                .stream()
                .map(indices -> indices.stream().map(list::get).collect(Collectors.toList()))
                .collect(Collectors.toList());
        int batch = 0;
        log.error("{}一共拆分为{}批次开始执行",taskName,partList.size());
        for (List<T> splitList :partList) {
            batch++;
            int finalBatch = batch;
            Instant readyStart = Instant.now();
            callables.add(new Callable<Object>() {
                @Override
                public Object call() throws Exception {
                    log.error("{}第{}批开始执行", taskName,finalBatch);
                    Instant start = Instant.now();
                    List<T> results = new ArrayList<>();
                    for (T t : splitList) {
                        T result = (T) myTask.work(t);
                        results.add(result);
                    }
                    Instant end = Instant.now();
                    log.error("{}第{}批执行结束,线程等待时间为{}(ms),当前批次执行耗时{}(ms)", taskName,finalBatch,Duration.between(readyStart,start).toMillis(),Duration.between(start,end).toMillis());
                    return results;
                }
            });
        }
        List<Future<Object>> futures = threadPoolExecutor.invokeAll(callables);
        List<List<T>> results = new ArrayList<>();
        for (Future future : futures) {
            List<T> o = (List<T>) future.get();
            results.add(o);
        }
        return results;
    }
}

下面代码为调用代码,调用逻辑为定义了一批data英雄分批次开启多线程进入战场大乱斗,最后各个英雄的耗时及战绩

package AtomicTest;

import disruptor.WwwApplication;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.CollectionUtils;

import java.io.InputStream;
import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;


/**
 * desc:
 * author:wangqiangac
 * date:2023/12/21 14:22
 */
@Slf4j
@SpringBootTest(classes=WwwApplication.class)
public class ThreadPoolTest {
    public static  List<String> list = new ArrayList<>();
    static{
        list.add("斧王");list.add("剑圣");
        list.add("小黑");list.add("幽鬼");
        list.add("火枪");list.add("大屁股");
        list.add("哈市卡");list.add("虚空假面");
        list.add("蓝胖");list.add("路西法");
        list.add("美杜莎");list.add("巫医");
        list.add("莱恩");list.add("冰女");
        list.add("幻影刺客");list.add("圣堂刺客");
        list.add("火女");list.add("潮汐");
        list.add("赏金猎人");list.add("蓝猫");
    }
    @Test
    public void test() throws InterruptedException, ExecutionException {
        ThreadPoolBuilder threadPoolBuilder = new ThreadPoolBuilder();
        threadPoolBuilder.setCorePoolSize(5).setMaxPoolSize(20).setKeepAliveSeconds(120).setQueueCapacity(20)
                .setThreadNamePrefix("王强的线程").setPrestartAllCoreThreads(true).setRejectedExecutionHandler(new BlockPolicy());
        ThreadPoolExecutor threadPoolExecutor = threadPoolBuilder.builderThreadPoolExecutor();

        List<List<String>> results = ThreadUtil.bathCalc(threadPoolExecutor, list, 5, "决战冰封之巅任务",new MyTask() {
            @Override
            public <T> String work(T www) throws Exception {
                Instant start = Instant.now();
                log.error(www.toString()+"切入战场");
                Thread.sleep(new Random().nextInt(20) * 1000);
                Instant end = Instant.now();
                long l = Duration.between(start, end).toMillis();
                log.error(www.toString()+"战场耗时(ms):"+l);
                return www.toString() + "获得" + new Random().nextInt(10) + "血";
            }
        });
        log.error("各个英雄最终获得成就如下:");
        for (List<String> list1:results) {
            log.error("当前队伍获取成绩如下:");
            for (String name: list1) {
                log.error(name);
            }
        }

    }
}

最终运行结果如下

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值