开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情
一次性任务
for 循环插入数据的特点:
- 频繁建立和释放数据库连接(用批量查询解决)
- for 循环是绝对线性的(可以并发提速)
注意:并发时不要用到非并发类的集合
获取核心线程数
private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors() - 1;
线程池的参数包括:
- corePoolSize 线程池核心线程大小 核心线程即即使是空闲时间也不会被销毁,线程池将维持最小的线程数量,即核心线程数
- maximumPoolSize 线程池最大线程数量 当线程数达到核心线程数 corePoolSize 时,会将新提交的任务缓存到工作队列中。若工作队列满了,会创建新的线程进行处理,所创建的线程数量由 maximumPoolSize 指定。
- keepAliveTime 空闲线程存活时间 当线程数大于核心线程数 corePoolSize 时,并且有线程空闲,通过设置 keepAliveTime 时间来确定销毁线程。
- unit 空闲线程存活时间单位 keepAliveTime 的计量单位。
- workQueue 工作队列 新的任务会进入工作队列,任务调度时从队列取出任务。
- threadFactory 线程工厂 使用线程工厂来创建线程,可以设定线程名等。
- 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());
}