list.parallelStream()并行流
Java中List的parallelStream线程流技术,可以很方便的帮我实现多线程,下面结合一个案例来看看效率的优化
业务
向数据库写入大数据
完整测试代码
package com.example.mysql.controller;
import com.example.mysql.entity.User;
import com.example.mysql.mapper.UserMapper;
import com.example.mysql.util.DateUtil;
import com.example.mysql.util.NumberUtil;
import com.example.mysql.util.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.ListUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
@RestController
@Slf4j
public class MySqlIndexController {
@Resource
private UserMapper userMapper;
private ExecutorService executorService = Executors.newFixedThreadPool(20);
@GetMapping("/mysql")
public String mysql() throws ParseException {
long st = System.currentTimeMillis();
int total = 0;
for (int i = 0; i < 50; i++) {
List<User> users = new ArrayList<>();
for (int j = 0; j < 100000; j++) {
User user = new User();
user.setId(UUIDUtil.nextUUID());
user.setUsername(UUIDUtil.nextUUID());
user.setPassword(UUIDUtil.nextUUID());
user.setAddress(UUIDUtil.nextUUID());
user.setEmail(UUIDUtil.nextUUID());
user.setProfile(UUIDUtil.nextUUID());
user.setBirthday(DateUtil.randomDate());
user.setRegisterDay(DateUtil.randomDate());
user.setLoginDay(DateUtil.randomDate());
user.setStatus(NumberUtil.getStatusInt());
user.setAccount(NumberUtil.accountDecimal());
user.setBalance(NumberUtil.balanceDecimal());
user.setAge(NumberUtil.getAgeInt());
user.setSex(NumberUtil.getSexInt());
user.setAvatar(UUIDUtil.nextUUID());
user.setLevel(NumberUtil.getLevelInt());
user.setParamOne(UUIDUtil.nextUUID());
users.add(user);
}
total += users.size();
List<List<User>> partition = ListUtils.partition(users, 500);
log.info("正在写入数据库,第 {} 轮", (i+1));
AtomicInteger count = new AtomicInteger();
int tempI = (i+1);
partition.parallelStream().forEach(p -> {
userMapper.insertBatch(p);
log.error("写完第 {} 批,批次 {}/{},第 {} 轮", count.getAndIncrement() + 1, count.get(), partition.size(), tempI);
});
log.info("第 {} 轮写入数据库结束", (i+1));
}
log.error("{} 条数据插入数据库,耗时:{} s", total, (System.currentTimeMillis() - st) / 1000);
return "success";
}
}
//partition.forEach(p -> {
// executorService.execute(() -> {
// log.error("写完第 {} 批,批次 {}/{}", count.getAndIncrement(), count.get(), partition.size());
// userMapper.insertBatch(p);
// });
// });
测试:100000数据
使用MyBatis框架向MySQL写入100000条数据,对比使用parallelStream和没有使用parallelStream效率
不使用parallelStream,代码
log.info("正在写入数据库,第 {} 轮", i);
AtomicInteger count = new AtomicInteger();
int tempI = i;
partition.forEach(p -> {
log.error("写完第 {} 批,批次 {}/{},第 {} 轮", count.getAndIncrement(), count.get(), partition.size(), tempI);
userMapper.insertBatch(p);
});
log.info("第 {} 轮写入数据库结束", i);
耗时157s
使用parallelStream,代码
log.info("正在写入数据库,第 {} 轮", i);
AtomicInteger count = new AtomicInteger();
int tempI = i;
partition.parallelStream().forEach(p -> {
log.error("写完第 {} 批,批次 {}/{},第 {} 轮", count.getAndIncrement(), count.get(), partition.size(), tempI);
userMapper.insertBatch(p);
});
log.info("第 {} 轮写入数据库结束", i);
耗时100s
测试:500000数据
使用更多的数据测试
不使用parallelStream
使用parallelStream
总结
使用parallelStream会比没有使用parallelStream的速度更快,如果数据量大的话,会有翻倍的性能提升,如下
state | cost | nums |
---|---|---|
no parallelStream | 157s | 100000 |
parallelStream | 100s | 100000 |
no parallelStream | 750s | 500000 |
parallelStream | 539s | 500000 |