【无标题】

Java多线程处理并汇总处理结果

简介

在业务逻辑处理时,经常会for循环处理多条数据(如2000条),当每一条数据处理时间略微长时(0.1s),整体时间就会比较长了(200s)

如果开10个线程处理时,整体时间就可以缩短10倍,整体只需要20s就可以完成了

因此,本篇文章就介绍一下如何更好的多线程处理,以缩短处理时间

1、示例代码

javascript 复制代码
package ins.claim.simplecase.service;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListeningExecutorService;
import ins.platform.common.util.CompletableFutureUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Slf4j
@Service
public class TestThreadService {

    @Autowired
    private ListeningExecutorService threadPool;


    /**
     * @Author Author 
     * @Description 测试入口
     * @Date 16:58 2023/11/24
     * @Param []
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     **/
    public List<Map<String, String>> test(){
        // 1、组装请求数据
        List<Map<String, String>> dataList = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            Map<String, String> map = new HashMap<>();
            map.put("1", "0");
            dataList.add(map);
        }
        // 2、多线程处理数据,并返回
        return this.deal(dataList);
    }

    /**
     * @Author Author 
     * @Description 分开多次线程处理
     * @Date 16:50 2023/11/24
     * @Param [list]
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     **/
    private List<Map<String, String>> deal(List<Map<String, String>> list){
        // 处理后返回的结果数据
        List<Map<String, String>> result = new ArrayList<>();
        // 每个线程处理的数据条数(可以自定义)
        int threadSize = 50;
        // 判断是否要开线程,计算需要开的线程个数,并依次处理
        if (list != null && list.size() > threadSize)
        {
            List<CompletableFuture<List<Map<String, String>>>> threadResultList = Lists.newArrayList();
            // 计算开几个线程,每个线程处理 threadSize 条
            int threadCount = (list.size() / threadSize) + 1;
            // 循环需要开的线程个数,并处理
            for (int i = 0; i < threadCount; i++) {
                String rid = "日志信息" + i;
                log.info("多线程处理第{}个线程", i);
                if (i == threadCount - 1)
                {
                    // 最后一次 开线程 处理
                    List<Map<String, String>> subList = list.subList(threadSize * i, list.size());
                    threadResultList.add(CompletableFuture.supplyAsync(() -> threadDealOnce(subList, rid), threadPool).exceptionally(ex -> {
                        log.error("线程处理失败了,请查看原因:", ex);
                        return null;
                    }));
                }
                else
                {
                    // 开单个线程
                    List<Map<String, String>> subList = list.subList(threadSize * i, threadSize * (i + 1));
                    threadResultList.add(CompletableFuture.supplyAsync(() -> threadDealOnce(subList, rid), threadPool).exceptionally(ex -> {
                        log.error("线程处理失败了,请查看原因:", ex);
                        return null;
                    }));
                }
            }

            // 多个线程处理后,结果合并处理
            for (List<Map<String, String>> resultOnce : CompletableFutureUtils.futureList(threadResultList)) {
                result.addAll(resultOnce);
            }
        }
        else
        {
            // 不开线程,一次性处理
            result = threadDealOnce(list, "");
        }

        return result;
    }

    /**
     * @Author Author 
     * @Description 单次线程处理的方法(真实的业务方法)
     * @Date 16:49 2023/11/24
     * @Param [list, rid]
     * @return java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     **/
    private List<Map<String, String>> threadDealOnce(List<Map<String, String>> list, String rid){
        log.info("单次线程处理的方法日志开始{}", rid);
        if (list != null && list.size() > 0) {
            int i = 0;
            for (Map<String, String> map : list) {
                log.info("单次线程处理的方法日志{}", rid + "-" + i);
                map.put("2", rid + i);
                i++;
            }
        }

        return list;
    }
}
javascript 复制代码
/**
 * @Description 多线程处理数据测试
 * @return
 * @throws ParseException
 */
@RequestMapping("/testThreadDealData")
public List<Map<String, String>> testThreadDealData() throws Exception {
	List<Map<String, String>> result = testThreadService.test();
	return result;

}

2、线程池设置

服务启动时,需配置好线程池

javascript 复制代码
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.*;

/**
 * 在于Spring启动时自动加载一个ExecutorService对象. 得到一个20线程的线程池
 */
@Configuration
public class ThreadPoolConfig {

    /**
     * 该线程池使用的时候,不允许手动关闭,生命周期与应用绑定
     */
    @Bean
    public ListeningExecutorService getThreadPool(){
        ThreadFactory basicThreadFactory = new BasicThreadFactory.Builder().namingPattern("basicThreadFactory-ThreadPoolConfig").build();
        ListeningExecutorService pool = new MdcDelegateThreadPoolExecutor(MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(20, basicThreadFactory)));
        return pool;
    }

}
javascript 复制代码
import com.google.common.util.concurrent.AbstractListeningExecutorService;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.sinosoft.fragins.framework.constance.ClmConstants;
import com.sinosoft.fragins.framework.utils.UUIDUtils;
import org.slf4j.MDC;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

/**
 * @Author Author 
 * @Description MDC rid 线程委托类(目的:rid 区分一下 主线程和子线程)
 * @Date 16:00 2023/11/6
 * @Param 
 * @return 
 **/
public class MdcDelegateThreadPoolExecutor extends AbstractListeningExecutorService {

    private ListeningExecutorService delegate;


    public MdcDelegateThreadPoolExecutor(ListeningExecutorService delegate) {
        this.delegate = delegate;
    }

    @Override
    public ListenableFuture<?> submit(Runnable task) {
        return delegate.submit(new MdcDelegateRunnable(task));
    }

    @Override
    public <T> ListenableFuture<T> submit(Callable<T> task) {
        return delegate.submit(new MDCDelegateCallable<>(task));
    }

    @Override
    public void shutdown() {
        delegate.shutdown();
    }

    @Override
    public List<Runnable> shutdownNow() {
        return delegate.shutdownNow();
    }

    @Override
    public boolean isShutdown() {
        return delegate.isShutdown();
    }

    @Override
    public boolean isTerminated() {
        return delegate.isTerminated();
    }

    @Override
    public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
        return delegate.awaitTermination(timeout, unit);
    }

    @Override
    public void execute(Runnable command) {
        delegate.execute(command);
    }


    class MDCDelegateCallable<V> implements Callable<V> {

        private String mainThreadRid;

        private Callable<V> delegate;

        public MDCDelegateCallable(Callable<V> callable) {
            this.mainThreadRid = MDC.get(ClmConstants.TRACE_ID_KEY);
            this.delegate = callable;
        }

        @Override
        public V call() throws Exception {
            MDC.put(ClmConstants.TRACE_ID_KEY, mainThreadRid + "-" + UUIDUtils.get12UUID());
            V ret = delegate.call();
            MDC.remove(ClmConstants.TRACE_ID_KEY);
            return ret;
        }
    }

    class MdcDelegateRunnable implements Runnable {

        // 主线程rid
        private String mainThreadRid;

        private Runnable delegate;

        /**
         * @Author Author 
         * @Description 构造器
         * @Date 16:01 2023/11/6
         * @Param [runnable]
         * @return
         **/
        public MdcDelegateRunnable(Runnable runnable) {
            this.mainThreadRid = MDC.get(ClmConstants.TRACE_ID_KEY);
            this.delegate = runnable;
        }

        @Override
        public void run() {
            MDC.put(ClmConstants.TRACE_ID_KEY, mainThreadRid + "-" + UUIDUtils.get12UUID());
            delegate.run();
            MDC.remove(ClmConstants.TRACE_ID_KEY);
        }
    }
}
相关推荐
程序媛-徐师姐1 分钟前
Java 基于SpringBoot+vue框架的老年医疗保健网站
java·vue.js·spring boot·老年医疗保健·老年 医疗保健
yngsqq2 分钟前
c#使用高版本8.0步骤
java·前端·c#
流星白龙4 分钟前
【C++习题】10.反转字符串中的单词 lll
开发语言·c++
尘浮生12 分钟前
Java项目实战II基于微信小程序的校运会管理系统(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven·intellij-idea
MessiGo12 分钟前
Python 爬虫 (1)基础 | 基础操作
开发语言·python
小白不太白95016 分钟前
设计模式之 模板方法模式
java·设计模式·模板方法模式
Tech Synapse18 分钟前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
xoxo-Rachel24 分钟前
(超级详细!!!)解决“com.mysql.jdbc.Driver is deprecated”警告:详解与优化
java·数据库·mysql
乌啼霜满天24926 分钟前
JDBC编程---Java
java·开发语言·sql