ThreadPoolTaskExecutor多线程跑任务时数据未跑完

1.问题描述:循环查数据,然后用多线程去更新查到的数据

代码如下:

建表语句

CREATE TABLE `tb_user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `name` varchar(50) DEFAULT NULL COMMENT '名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

实体类

@Data
@TableName("tb_user")
public class TbUser {

    @TableId(type = IdType.AUTO)
    private int id;

    @TableField("name")
    private String name;
}

mapper

@Mapper
@Repository
public interface TbUserMapper extends BaseMapper<TbUser> {

    int updateByIds(List<TbUser> list);
}

xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.test.netty.mapper.TbUserMapper">

    <update id="updateByIds">

       <foreach collection="list" item="user" separator=";">
           update tb_user set name = #{user.name} where id = #{user.id}
       </foreach>
    </update>
</mapper>

service

package com.test.netty.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.test.netty.pojo.TbUser;

public interface UserService extends IService<TbUser> {
    void testThread();
}


实现类

package com.test.netty.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.test.netty.mapper.TbUserMapper;
import com.test.netty.pojo.TbUser;
import com.test.netty.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static com.baomidou.mybatisplus.extension.toolkit.Db.updateBatchById;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<TbUserMapper, TbUser> implements UserService {
    @Resource
    private TbUserMapper userMapper;
    
    @Autowired
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @Override
    public void testThread() {

        log.info("开始批量更新流程");
        long start = System.currentTimeMillis();
        Long count = userMapper.selectCount(null);
        Integer pageSize = 500;
        int totalPages = count.intValue()/pageSize;
        if (count.intValue() % pageSize ==0){
            totalPages ++;
        }

        for (int i=1;i<totalPages;i++){
            Page<TbUser> page = userMapper.selectPage(new Page<TbUser>(i, 500), null);
            List<TbUser> list = page.getRecords();
            List<TbUser> users = new ArrayList<>();

            list.stream().forEach(user->{
                user.setName("test6");
                users.add(user);
            });
            int finalI = i;
            taskExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("第{}次更新", finalI);
                    updateBatchById(users,500);
                    //userMapper.updateByIds(users);
                    log.info("第{}次更新完成", finalI);
                }
            });
        }
        

        log.info("所有数据更新完成,耗时:{}",System.currentTimeMillis()-start);
    }
}


线程池配置

package com.test.netty.config;

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

import java.util.Currency;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@Slf4j
public class TaskExecutorPoolConfig {

    private static final int workQueue = 5000;

    private static final int keepActiveTime = 30;

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();

    private static final int corePoolSize = 2 * CPU_COUNT;

    private static final int maxPoolSize = CPU_COUNT * 5;

    @Bean("taskExecutor")
    public ThreadPoolTaskExecutor myTaskExecutor(){
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setThreadNamePrefix("poolTest-");
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(workQueue);
        executor.setKeepAliveSeconds(keepActiveTime);
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

mybatisPlus分页配置

package com.test.netty.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

测试类

单元测试需要添加test依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
package com.test.netty.controller;

import com.test.netty.mapper.DataMapper;
import com.test.netty.mapper.TbUserMapper;
import com.test.netty.pojo.DataDto;
import com.test.netty.pojo.TbUser;
import com.test.netty.service.DataService;
import com.test.netty.service.TestService;
import com.test.netty.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Slf4j
class TestControllerTest {


    @Autowired
    private TbUserMapper userMapper;

    @Autowired
    private UserService userService;

    @Test
    void test1() {
    }

    @Test
    public void insertBatch(){
        for (int i=1;i<=10000;i++){
            TbUser user = new TbUser();
            user.setName(String.valueOf(i));
            userMapper.insert(user);
            log.info("第{}次插入",i);
        }
    }

    @Test
    public void test(){
        userService.testThread();
    }







}

application配置文件

server:
  port: 8081
spring:
  application:
    name: netty-test-01
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&allowMultiQueries=true
    username: root
    password: root

步骤:

1.先运行测试类insertBatch方法,插入数据10000条。
2.运行test方法进行更新

问题

发现执行完成后更新的数据不完整,只有五千左右
在这里插入图片描述
然后自己写sql:
将实现类的方法注解放开
在这里插入图片描述
运行后发现数据全部更新完成
在这里插入图片描述
注意:test7或test8是我每次运行都会改一下修改的值,这个随便写,不用在意。

解决方法:使用ExecutorService线程池

未找到出现这个问题的原因,但是换了一个线程池后问题解决
在实现类中创建线程池
在这里插入图片描述
在这里插入图片描述
完成代码如下:

package com.test.netty.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.test.netty.mapper.TbUserMapper;
import com.test.netty.pojo.TbUser;
import com.test.netty.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static com.baomidou.mybatisplus.extension.toolkit.Db.updateBatchById;

@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<TbUserMapper, TbUser> implements UserService {
    @Resource
    private TbUserMapper userMapper;

    @Autowired
    @Qualifier("taskExecutor")
    private ThreadPoolTaskExecutor taskExecutor;

    @Override
    public void testThread() {
        ExecutorService executorService = Executors.newFixedThreadPool(30);

        log.info("开始批量更新流程");
        long start = System.currentTimeMillis();
        Long count = userMapper.selectCount(null);
        Integer pageSize = 500;
        int totalPages = count.intValue()/pageSize;
        if (count.intValue() % pageSize ==0){
            totalPages ++;
        }

        for (int i=1;i<totalPages;i++){
            Page<TbUser> page = userMapper.selectPage(new Page<TbUser>(i, 500), null);
            List<TbUser> list = page.getRecords();
            List<TbUser> users = new ArrayList<>();

            list.stream().forEach(user->{
                user.setName("test6");
                users.add(user);
            });
            int finalI = i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    log.info("第{}次更新", finalI);
                    updateBatchById(users,500);
                    userMapper.updateByIds(users);
                    log.info("第{}次更新完成", finalI);
                }
            });
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        }catch (Exception e){
            log.error("error");
        }

        log.info("所有数据更新完成,耗时:{}",System.currentTimeMillis()-start);
    }
}

结果

无论使用哪一种方法,数据都能全部更新,ExecutorService会等待所有线程都结束后再去执行其他业务。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中可以使用ThreadPoolTaskExecutor来实现多线程处理批量数据入库的操作。ThreadPoolTaskExecutor是Spring框架提供的一个线程池实现类,可以方便地管理线程池的创建和销毁,并提供了一些配置参数来控制线程池的行为。 下面是使用ThreadPoolTaskExecutor实现多线程处理批量数据入库的步骤: 1. 首先,需要在项目中引入Spring的依赖,以使用ThreadPoolTaskExecutor类。可以在pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> ``` 2. 在代码中创建一个ThreadPoolTaskExecutor对象,并进行相关配置。可以通过在Spring配置文件中配置bean,或者使用Java代码进行配置。以下是一个示例配置: ```java @Configuration @EnableAsync public class ThreadPoolConfig { @Bean public ThreadPoolTaskExecutor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); // 设置核心线程数 executor.setMaxPoolSize(20); // 设置最大线程数 executor.setQueueCapacity(100); // 设置队列容量 executor.setThreadNamePrefix("MyThread-"); // 设置线程名前缀 executor.initialize(); // 初始化线程池 return executor; } } ``` 3. 在需要进行批量数据入库的地方,使用@Async注解标记方法,并指定使用线程池。例如: ```java @Service public class DataBatchService { @Autowired private ThreadPoolTaskExecutor taskExecutor; @Async("taskExecutor") public void processBatchData(List<Data> dataList) { // 批量数据入库的逻辑处理 // ... } } ``` 4. 调用processBatchData方法,会自动使用线程池中的线程进行处理。例如: ```java @Autowired private DataBatchService dataBatchService; public void batchDataInsert(List<Data> dataList) { dataBatchService.processBatchData(dataList); } ``` 这样就可以利用ThreadPoolTaskExecutor实现多线程处理批量数据入库了。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值