并发插入数据库数据

88 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

一次性任务

for 循环插入数据的特点:

  1. 频繁建立和释放数据库连接(用批量查询解决)
  2. for 循环是绝对线性的(可以并发提速)

注意:并发时不要用到非并发类的集合

获取核心线程数

 private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors() - 1;

线程池的参数包括:

  1. corePoolSize 线程池核心线程大小 核心线程即即使是空闲时间也不会被销毁,线程池将维持最小的线程数量,即核心线程数
  2. maximumPoolSize 线程池最大线程数量 当线程数达到核心线程数 corePoolSize 时,会将新提交的任务缓存到工作队列中。若工作队列满了,会创建新的线程进行处理,所创建的线程数量由 maximumPoolSize 指定。
  3. keepAliveTime 空闲线程存活时间 当线程数大于核心线程数 corePoolSize 时,并且有线程空闲,通过设置 keepAliveTime 时间来确定销毁线程。
  4. unit 空闲线程存活时间单位 keepAliveTime 的计量单位。
  5. workQueue 工作队列 新的任务会进入工作队列,任务调度时从队列取出任务。
  6. threadFactory 线程工厂 使用线程工厂来创建线程,可以设定线程名等。
  7. handler 拒绝策略 线程池数量达到最大控制数,并且工作队列也满了,如果有新的任务提交,需要采取拒绝策略。 建立执行器(线程池):
 private ExecutorService executorService = new ThreadPoolExecutor(AVAILABLE_PROCESSORS, 1000, 10000, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10000));

连接池的参数设置

  CPU 密集型:分配的核心线程数 = CPU - 1
  IO  密集型:分配的核心线程数可以大于 CPU 核数

建立一个测试类实现

 @SpringBootTest
 public class InsertUsersTest {
     @Resource
     UserService userService;
     // CPU 密集型:分配的核心线程数 = CPU - 1
     // IO  密集型:分配的核心线程数可以大于 CPU 核数
     private ExecutorService executorService = new ThreadPoolExecutor(40, 1000, 10000, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10000));
 ​
     /**
      * 批量插入用户
      */
     @Test
     public void doInsertUsers() {
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
         final int INSERT_NUM = 100000;
         List<User> userList = new ArrayList<>();
         for (int i = 0; i < INSERT_NUM; i++) {
             User user = new User();
             user.setUsername("名称");
             user.setUserAccount("fucky");
             user.setGender(0);
             user.setUserPassword("12345678");
             user.setPhone("18963421945");
             user.setEmail("123456@qq.com");
             userList.add(user);
         }
         userService.saveBatch(userList, 10000);
         stopWatch.stop();
         System.out.println(stopWatch.getTotalTimeMillis());
     }
 ​
 }
 ​

并发插入数据

     // CPU 密集型:分配的核心线程数 = CPU - 1
     // IO  密集型:分配的核心线程数可以大于 CPU 核数
     private ExecutorService executorService = new ThreadPoolExecutor(40, 1000, 10000, TimeUnit.MINUTES, new ArrayBlockingQueue<>(10000));
     /**
      * 并发插入用户
      */
     @Test
     public void doConcurrencyInsertUsers() {
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
         final int INSERT_NUM = 100000;
         //分十组
         int j = 0;
         int batchSize = 5000;
         //因为CompletableFuture实现了Future接口,我们先来回顾Future吧。
         //Future是Java5新加的一个接口,它提供了一种异步并行计算的功能。如果主线程需要执行一个很耗时的计算任务,我们就可以通过future把这个任务放到异步线程中执行。主线程继续处理其他任务,处理完成后,再通过Future获取计算结果。
         List<CompletableFuture<Void>> futureList = new ArrayList<>();
         for(int i = 0; i < 10; i++){
             List<User> userList = new ArrayList<>();
             while(true){
                 j++;
                 User user = new User();
                 user.setUsername("fucky");
                 user.setGender(0);
                 user.setUserPassword("12345678");
                 user.setPhone("18963421945");
                 user.setEmail("123456@qq.com");
                 userList.add(user);
                 if(j % batchSize == 0){
                     break;
                 }
             }
             //新建异步任务,异步执行
             CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                 System.out.println("ThreadName:" + Thread.currentThread().getName());
                 userService.saveBatch(userList, batchSize);
             },executorService);
             futureList.add(future);
         }
         CompletableFuture.allOf(futureList.toArray(new CompletableFuture[]{})).join();
         stopWatch.stop();
         //输出执行时间
         System.out.println(stopWatch.getTotalTimeMillis());
     }