SpringBoot利用ThreadPoolTaskExecutor 线程池实现批量插入数据实例

679 阅读2分钟

1. 添加线程池配置

import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池的配置
 */
@Slf4j
@EnableAsync
@Configuration
public class ExecutorConfig {

    @Value("${async.executor.thread.core_pool_size}")
    private int corePoolSize;
    @Value("${async.executor.thread.max_pool_size}")
    private int maxPoolSize;
    @Value("${async.executor.thread.queue_capacity}")
    private int queueCapacity;
    @Value("${async.executor.thread.name.prefix}")
    private String namePrefix;
 
    @Bean(name = "asyncServiceExecutor")
    public Executor asyncServiceExecutor() {
        log.warn("start asyncServiceExecutor");
        //在这里修改
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        // rejection-policy:当pool已经达到max size的时候,如何处理新任务
        // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        return executor;
    }
}

2. 添加配置 application.properties

# 异步线程配置
# 配置核心线程数
async.executor.thread.core_pool_size = 30
# 配置最大线程数
async.executor.thread.max_pool_size = 30
# 配置队列大小
async.executor.thread.queue_capacity = 99988
# 配置线程池中的线程的名称前缀
async.executor.thread.name.prefix = async-

3. 创建 AsyncService 和 AsyncServiceImpl 实例

import java.util.List;
import java.util.concurrent.CountDownLatch;

public interface AsyncService {

     void executeAsync(List<OneTransOrder> results, OneTransOrderDao oneTransOrderDao, CountDownLatch countDownLatch);
}

@Service
@Slf4j
public class AsyncServiceImpl implements AsyncService {



    @Override
    @Async("asyncServiceExecutor")
    public void executeAsync(List<OneTransOrder> results, OneTransOrderDao oneTransOrderDao, CountDownLatch countDownLatch) {
        try{
            log.warn("start executeAsync");
            //异步线程要做的事情
            oneTransOrderDao.insertBatch(results);
            log.warn("end executeAsync");
        }finally {
            countDownLatch.countDown();// 很关键, 无论上面程序是否异常必须执行countDown,否则await无法释放
        }
    }
}

4. 实现业务类的调用示例

import cn.hutool.core.collection.ListUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 *
 * @author 
 * @since 2022-07-15 13:42:00
 */
@Service("oneTransOrderService")
public class OneTransOrderServiceImpl extends ServiceImpl<OneTransOrderDao, OneTransOrder> implements OneTransOrderService {

    @Resource
    private AsyncService asyncService;

    @Resource
    private OneTransOrderDao oneTransOrderDao;


    public int testMultiThread() {
        List<OneTransOrder> oneTransOrders = oneTransOrderDao.selectList(new QueryWrapper<>());
        //测试每100条数据插入开一个线程
        List<List<OneTransOrder>> lists = ListUtil.split(oneTransOrders, 100);
        CountDownLatch countDownLatch = new CountDownLatch(lists.size());
        for (List<OneTransOrder> listSub:lists) {
            asyncService.executeAsync(listSub, oneTransOrderDao,countDownLatch);
        }
        try {
            countDownLatch.await(); //保证之前的所有的线程都执行完成,才会走下面的;
            // 这样就可以在下面拿到所有线程执行完的集合结果
        } catch (Exception e) {
            log.error("阻塞异常:"+e.getMessage());
        }
        return oneTransOrders.size();
    }

}

5. 持久化层

public interface OneTransOrderDao extends BaseMapper<OneTransOrder> {

    /**
     * 批量新增数据(MyBatis原生foreach方法)
     *
     * @param entities List<OneTransOrder> 实例对象列表
     * @return 影响行数
     */
    int insertBatch(@Param("entities") List<OneTransOrder> entities);
}


    <!-- 批量插入 -->
    <insert id="insertBatch" keyProperty="fId" useGeneratedKeys="true">
        insert into OnePayOrder(music_id )
        values
        <foreach collection="entities" item="entity" separator=",">
            (#{entity.musicId})
        </foreach>
    </insert>