使用线程池 插入万条数据做压力测试

实习遇到压力测试自己写一个程序做压力测试 ,使用线程池教程

1.在yml文件中添加一个线程池的配置

# 线程池基础参数
task:
  pool:
    corePoolSize: 8 # 核心线程数
    maxPoolSize: 20 # 设置最大线程数
    keepAliveSeconds: 300 # 设置线程活跃时间
    queueCapacity: 9999 # 设置队列容量

 2在Spring中配置一个Bean,作为线程池的 配置bean 将yml中的参数拿出来

//设置yml中的前缀 task.pool

@ConfigurationProperties(prefix = "task.pool")
public class TaskThreadInfo {
    private Integer corePoolSize;
    private Integer maxPoolSize;
    private Integer keepAliveSeconds;
    private Integer queueCapacity;

    public Integer getCorePoolSize() {
        return corePoolSize;
    }

    public void setCorePoolSize(Integer corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public Integer getMaxPoolSize() {
        return maxPoolSize;
    }

    public void setMaxPoolSize(Integer maxPoolSize) {
        this.maxPoolSize = maxPoolSize;
    }

    public Integer getKeepAliveSeconds() {
        return keepAliveSeconds;
    }

    public void setKeepAliveSeconds(Integer keepAliveSeconds) {
        this.keepAliveSeconds = keepAliveSeconds;
    }

    public Integer getQueueCapacity() {
        return queueCapacity;
    }

    public void setQueueCapacity(Integer queueCapacity) {
        this.queueCapacity = queueCapacity;
    }

}

 3.在新建一个配置TaskExecutePoolConfig.java类,将ThreadPoolTaskExecutor 实现,具体代码如下

@Configuration
@EnableConfigurationProperties(TaskThreadInfo.class)
public class TaskExecutePoolConfig {
    private TaskThreadInfo taskThreadInfo;
    public TaskExecutePoolConfig(TaskThreadInfo info){
        this.taskThreadInfo = info;
    }
    /**
     * 线程池配置
     */
    @Bean(name = "threadPoolTaskExecutor",destroyMethod = "shutdown")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //设置最大 的 核心线程数
        taskExecutor.setCorePoolSize(taskThreadInfo.getCorePoolSize());
        //设置最大线程数
        taskExecutor.setMaxPoolSize(taskThreadInfo.getMaxPoolSize());
        //设置 线程空闲时间
        taskExecutor.setKeepAliveSeconds(taskThreadInfo.getKeepAliveSeconds());
        //设置最大队列
        taskExecutor.setQueueCapacity(taskThreadInfo.getQueueCapacity());
        //设置拒绝策略
        taskExecutor.setRejectedExecutionHandler(rejectedExecutionHandler());
        //设置线程前缀名字
        taskExecutor.setThreadNamePrefix("多表插入-Async-");
        taskExecutor.initialize();
        return taskExecutor;
    }
    //自定义线程拒绝策略


    @Bean
    public  RejectedExecutionHandler rejectedExecutionHandler(){
        RejectedExecutionHandler errorHandler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("线程池异常");
            }
        };
        return errorHandler;
    }

}

4实现方法

4.1 企业级开发往往设计多表联查,或者多表关联。所以在保存数据时需要使用到list集合。作为新手小白,我对于这个list同步问题可谓是伤透脑筋,

4.2使用 Collections.synchronizedList(new ArrayList<>());使用集合安全的list进行保存

List<RecUser> list = Collections.synchronizedList(new ArrayList<>());

4.3对其上锁Lock lock = new ReentrantLock();保存插入时不会丢失数据

 Lock lock = new ReentrantLock();

//上锁
lock.lock();
try {
  list.add(recUser);
} finally {
解锁
 lock.unlock();
 }

4.4创建CountDownLatch对象.通过使用CountDownLatch,主线程会等待所有线程执行完毕后再返回list,确保返回的list不为空。

CountDownLatch latch = new CountDownLatch(number); // 
 for (int count = 0; count < number; count++) {
        final int index = i;
        executor.execute(() -> {
            // 省略部分代码...

            lock.lock();
            try {
                list.add(recUser);
            } finally {
                lock.unlock();
            }
            log.info("当前线程池的数量为: {}", executor.getPoolSize());

            latch.countDown(); // 减少计数器的值
        });
    }

     try {
         latch.await();// 等待计数器变为0
        }catch (Exception e){
            e.getMessage();
        }
    log.info("执行线程成功");
    System.out.println("rec_user插入成功!");
    return list;
}

5.使用的总体完成效果



@Service
@Slf4j
public class RecUserServiceImpl extends ServiceImpl<RecUserMapper, RecUser> implements RecUserService {

    @Autowired
    private RecUserMapper recUserMapper;
    @Autowired
    private ThreadPoolTaskExecutor executor;

    public List<RecUser> saveIntoRecUser(int number){
        List<RecUser> list = Collections.synchronizedList(new ArrayList<>());
        Lock lock = new ReentrantLock();
        CountDownLatch latch = new CountDownLatch(number); // 创建CountDownLatch对象
        int i = 0 ;
        log.info("线程池初始化大小为{}",executor.getPoolSize());
        for (int count = 0 ; count < number ;count++){
            final int index = i;
             executor.execute(()->{
                    RecUser recUser = new RecUser();
                    recUser.setUserName("张三" + index);
                   
                    lock.lock();
                    try {
                        list.add(recUser);
                    } finally {
                        lock.unlock();
                    }
                    log.info("当前线程池的数量为: {}",executor.getPoolSize());
                    latch.countDown(); // 减少计数器的值
            });
        }

        try {
            latch.await();// 等待计数器变为0
        }catch (Exception e){
            e.getMessage();
        }
        log.info("执行线程成功");
        this.saveBatch(list);
        System.out.println("rec_user插入成功!");
        return list;
    }

}

6小tips 如果使用guava包中的Lists.partition方法对list切割会更快

         <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>29.0-jre</version>
        </dependency>
     this.saveBatch(recA01s);



        Lists.partition(recA01s,20).forEach(item ->{
            this.saveBatch(item);
        });

7效果展示

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值