实际开发中线程池的使用,批量插入数据

# 实际开发中线程池的使用,批量插入数据
	线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
	说明:Executor返回的线程池对象的弊端如下:
	(1)FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
	(2)CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
	以上是阿里巴巴Java开发手册中的规约。所以下面我用ThreadPoolExecutor的方式,来创建线程池,实际案例:向数据库中批量插入数据。
	**方式一:在方法中手动创建线程池**
       /**
     * 使用线程池来插入数据
     * @param users 我这里设计了10000条数据测试
     * @return
     */
    public String addAllUsers(List<MyUser> users){
        String sql = "insert into myuser(id,user_name,age,address,sex) values(?,?,?,?,?)";
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(5,10,10,
                        TimeUnit.MINUTES,new LinkedBlockingDeque<>(),new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < users.size(); i++) {
            MyUser myUser = users.get(i);
            threadPoolExecutor.execute(()->{
                try {
                    String name = threadPoolExecutor.getClass().getName();
                    log.info("线程"+name+"在工作");
                    String id = UUID.randomUUID().toString();
                    jdbcTemplate.update(sql,id,myUser.getName(),myUser.getAge(),myUser.getAddress(), myUser.getSex());
                }catch (Exception e){
                    e.printStackTrace();
                }
            });
        }
        //终止线程池
        threadPoolExecutor.shutdown();
        return "success";
    }
	![在这里插入图片描述](https://img-blog.csdnimg.cn/d015bacb225b464087546cfbc329500e.png#pic_center)
	**方式二:项目中常用,需要用创建一个公共组件供大家调用**
	1.创建线程池配置类
package com.example.demo.common;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;

/**
 * @Date:2023/3/5-15:54
 * @Description:连接池配置类
 * @version:1.0
 */
@Configuration
public class ThreadPoolConfig {
    //核心线程数
    private volatile int corePoolSize = 10;
    //最大线程数
    private volatile int maximumPoolSize = 30;
    //空闲线程最大存活时间
    private volatile int keepAliveTime = 100;
    //阻塞队列
    private static final BlockingDeque defaultBlockDeque = new LinkedBlockingDeque(100000);
    //默认的拒绝策略
    private static final RejectedExecutionHandler defaultHandler =
            new ThreadPoolExecutor.AbortPolicy();
    @Bean(name = "ScmThreadPoolExecutor")
    public ThreadPoolExecutor getThreadPoolTaskExecutor(){
        ThreadPoolExecutor threadPoolExecutor =
                new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,
                        TimeUnit.SECONDS,defaultBlockDeque,defaultHandler);
        return threadPoolExecutor;
    }
}

	2.从Spring容器中获取bean
package com.example.demo.common;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * @Date:2023/3/5-16:15
 * @Description:从Spring容器中获取Bean
 * @version:1.0
 */
@Component
public class SpringContextHelper implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHelper.applicationContext = applicationContext;
    }
    //根据类获取bean
    public static Object getBean(Class<?> clazz) throws BeansException{
        return applicationContext.getBean(clazz);
    }
    //根据名称获取bean
    public static Object getBean(String name) throws BeansException{
        return applicationContext.getBean(name);
    }
}
	3.通过反射执行方法的工具类
package com.example.demo.common;


import lombok.extern.log4j.Log4j2;

import java.lang.reflect.Method;

/**
 * @Date:2023/3/5-16:20
 * @Description:通过反射执行方法的工具类
 * @version:1.0
 */
@Log4j2
public class ReflectionUtil {
    /**
     *
     * @param object 服务对象
     * @param methodName 方法名称
     * @param args 参数
     * @throws Exception
     */
    public static void invokeMethod(Object object, String methodName, Object[] args) throws Exception{
        log.debug("invokeMethod start : 服务对象={},调用方法={}",new Object[]{object,methodName} );
        Class<?>[] paramClasses = null;
        if (args.length > 0){
            paramClasses = new Class<?>[args.length];
            for (int i = 0; i < args.length; i++) {
                paramClasses[i] = args[i].getClass();
            }
        }
        Method method = object.getClass().getMethod(methodName, paramClasses);
        method.setAccessible(true);
        method.invoke(object,args);
        log.debug("invokeMethod end ");

    }
}

	4.异步任务类
package com.example.demo.common;

/**
 * @Date:2023/3/5-16:40
 * @Description:异步任务类
 * @version:1.0
 */
public class AsyncTask implements Runnable{
    //服务对象
    private Object object;
    //服务方法
    private String methodName;
    //方法参数
    private Object[] args;

    public AsyncTask(Object object,String methodName,Object[] args){
        this.object = object;
        this.methodName = methodName;
        this.args = args;

    }
    @Override
    public void run() {
        try {
        	//这里就用到了反射来进行执行具体的方法
            ReflectionUtil.invokeMethod(object,methodName,args);
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}


	5.线程池使用工具类
package com.example.demo.common;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Date:2023/3/5-16:44
 * @Description:com.example.demo.common
 * @version:1.0
 */
public class AsyncTaskUtil {

    private volatile static ThreadPoolExecutor executor;

    private static final ReentrantLock LOCK = new ReentrantLock();

    /**
     * 获取线程池
     * @return
     */
    private static ThreadPoolExecutor getThreadPoolExecutor(){
        if (executor == null){
            LOCK.lock();
            try {
                if (executor == null){
                    executor = (ThreadPoolExecutor)SpringContextHelper.getBean("ScmThreadPoolExecutor");
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                LOCK.unlock();
            }
        }
        return executor;
    }

    public static void asyncTask(Object object,String methodName,Object[] args){
        AsyncTask asyncTask = new AsyncTask(object, methodName, args);
        asyncTask(asyncTask);
    }

    public static void asyncTask(Runnable asyncTask){
        AsyncTaskUtil.getThreadPoolExecutor().execute(asyncTask);
    }

    public static <T> Future<T> asyncTask(Callable<T> callableTask){
        return AsyncTaskUtil.getThreadPoolExecutor().submit(callableTask);
    }
}

6.在业务中真正的开始使用上面的基础工具类
(1)调用方式一:将要异步执行的代码单独写一个类,并且这个类继承Runnable接口

	创建一个类
package com.example.demo.service;

import com.example.demo.dto.MyUser;
import lombok.extern.log4j.Log4j2;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.UUID;

/**
 * @Date:2023/3/5-20:09
 * @Description:com.example.demo.service
 * @version:1.0
 */
@Log4j2
@Service
public class AddUsers implements Runnable{

    private MyUser myUser;

    private JdbcTemplate jdbcTemplate;

    public AddUsers(MyUser myUser, JdbcTemplate jdbcTemplate){
        this.myUser = myUser;
        this.jdbcTemplate = jdbcTemplate;
    }
    public AddUsers(){}

    @Override
    public void run() {
        try {
        	//具体插入数据库要执行的方法代码
            insertUser(myUser);
        }catch (Exception e){
            e.printStackTrace();
        }finally {

        }

    }
    /**
     * 插入一条数据
     * @param user
     */
    public void insertUser(MyUser user){
        String sql = "insert into myuser(id,user_name,age,address,sex) values(?,?,?,?,?)";
        String id = UUID.randomUUID().toString();
        jdbcTemplate.update(sql,id,user.getName(),user.getAge(),user.getAddress(), user.getSex());
    }

}

	具体service层进行调用:
    public String addUsers(List<MyUser> users){
        log.info("数据插入--start");
        for (int i = 0; i < users.size(); i++) {
            try {
                AsyncTaskUtil.asyncTask(new AddUsers(users.get(i),jdbcTemplate));
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        log.info("数据插入结束");
        return "seccess";
    }

(1)调用方式二:使用反射机制来调用

  public String addAllUsersOthersMethod(List<MyUser> users){
        log.info("数据插入--start");
        //从容器中获取到当前类的bean对象,不仅获取了当前类的bean,并且将自动装配的其他类也加载上。
        //直接new UserService 的话,那么在你下面需要调用的insertUser就无法使用你注入的类。
        UserService userService = (UserService)SpringContextHelper.getBean(UserService.class);
        for (int i = 0; i < users.size(); i++) {
            try {
            	//具体参数为:类,方法,方法参数
                AsyncTaskUtil.asyncTask(userService,"insertUser",new MyUser[]{users.get(i)});
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        log.info("数据插入结束");
        return "success";
    }

    /**
     * 插入一条数据
     * @param myUser
     */
    public void insertUser(MyUser myUser){
        String sql = "insert into myuser(id,user_name,age,address,sex) values(?,?,?,?,?)";
        String id = UUID.randomUUID().toString();
        jdbcTemplate.update(sql,id,myUser.getName(),myUser.getAge(),myUser.getAddress(), myUser.getSex());
    }

	以上三种方式均可以将大数据量异步的插入到数据库中。
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
线程池可以用来优化向数据批量插入数据的性能。以下是一种简单的实现方式: 1. 创建一个线程池线程池的大小根据你的机器性能和数据库负载情况来配置。 2. 将需要插入的数据按照一定的规则划分成多个小批次。 3. 创建一个任务队列,将每个小批次的数据都封装成一个任务,并放入任务队列。 4. 线程池的每个线程从任务队列取出一个任务,执行该任务的插入操作。 5. 执行完任务后,线程将结果返回给主线程,并继续从任务队列取出下一个任务执行。 6. 主线程等待所有任务执行完毕后,关闭线程池。 以下是一个简单的Java代码示例: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class DatabaseInsertThreadPool { private ExecutorService executorService; public DatabaseInsertThreadPool(int poolSize) { executorService = Executors.newFixedThreadPool(poolSize); } public void insertDataToDatabase(Data data) { // 将数据按照一定的规则划分成多个小批次 List<List<Data>> dataBatches = splitDataIntoBatches(data); // 将每个小批次的数据都封装成一个任务,并放入任务队列 for (List<Data> dataBatch : dataBatches) { executorService.submit(() -> { // 执行该任务的插入操作 insertDataBatchToDatabase(dataBatch); }); } } public void shutdown() { executorService.shutdown(); } private List<List<Data>> splitDataIntoBatches(Data data) { // 将数据按照一定的规则划分成多个小批次 ... } private void insertDataBatchToDatabase(List<Data> dataBatch) { // 将数据批量插入数据 ... } } ``` 你可以根据自己的具体需求来修改代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值