提起多线程,开发的小伙伴都非常熟悉,今天我们来总结一下在企业中,我们是如何去使用线程和多线程去是我们的程序效率变快的!
介绍线程和多线程概念的文章有很多,今天我们举一个实际的案例来演示下!
案例:用户表中有10000条数据,假设我们需要一条一条的去更新用户的信息(一个接口全量更新用户的数据),然后再返回给客户端更新成功,那么怎么样可以提高我们的下效率呢?显然,如果用同步请求的方式,那实在是太慢了,客户估计还没等到程序跑完就已经开始跟你的产品经理投诉了!
我们有三种方案
1).使用mq消息进行业务解耦,将更新数据的操作交给mq消费者去做处理,接口直接返回给客户,告诉他处理成功!
2).后端接口写业务的时候直接开一个线程,让更新数据的操作在线程里面进行处理,然后返回处理成功给客户端!
3).后端接口开线程池,每个线程处理100个(或者更多的数据),然后做数据的聚合操作,全部处理完成以后再返回客户端数据处理成功!
方案1的代码就不写了,不是本文关心的主题。
方案2的代码如下:
/**
* 使用单线程异步更新用户信息
* @return
* @throws InterruptedException
*/
@RequestMapping(value = "/updateUserInfo", method = RequestMethod.GET)
public String updateUserInfo() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
//查询出用户表的前2000条数据,对这些数据做更新操作
List<Users> users=usersService.getUsersByCount(2000);
for (Users user:users){
String userName = user.getUserName().replace("帅哥","美女");
user.setUserName(userName);
usersService.updateById(user);
}
}
});
thread.start();
return "completed";
}
方案3的代码如下:
/**
* 批量更新用户信息
* @return
* @throws InterruptedException
*/
@RequestMapping(value = "/updateBath", method = RequestMethod.GET)
public String updateBath() {
List<Future<Void>> futures = new ArrayList<>();
//查询出用户表的前2000条数据,对这些数据做更新操作
List<Users> users=usersService.getUsersByCount(2000);
//将这20000条数据放到线程池里面做批量更新,将美女修改成帅哥
int index = 0;
List<Users> tempusers = new ArrayList<>();
for(Users user:users){
index++;
tempusers.add(user);
//每100条数据一组,放到线程池里面去做处理
if (index % 100 == 0 || index == users.size()) {
List<Users> tempusers2 = new ArrayList<>(Arrays.asList(new Users[tempusers.size()]));
Collections.copy(tempusers2, tempusers);
while (true) {
try{
Future<Void> futureRes = userHandler.updateBath(tempusers2);
futures.add(futureRes);
tempusers.clear();
break;
}catch(Exception e){
log.error("线程池已满,等待1秒");
try {
Thread.sleep(1000*5);
} catch (InterruptedException e1) {
log.error("InterruptedException!!!");
}
}
}
}
}
log.warn("updateBath 线程数:{}", futures.size());
for (Future<Void> future : futures) {
try {
future.get();
} catch (InterruptedException e) {
log.error("updateBath 异步结果获取异常01!msg==>{}", ExceptionUtils.getStackTrace(e));
} catch (ExecutionException e) {
log.error("updateBath 异步结果获取异常02!msg==>{}", ExceptionUtils.getStackTrace(e));
}
}
return "completed";
}
其中userHandler.updateBath方法里面的代码如下:
@Async("testTaskExecutor")
public Future<Void> updateBath(List<Users> users) {
if (CollectionUtils.isEmpty(users)){
return new AsyncResult<>(null);
}
for (Users user:users){
String userName = user.getUserName().replace("美女","帅哥");
user.setUserName(userName);
usersService.updateById(user);
}
return new AsyncResult<>(null);
}
当然可以使用Future的话,也可以使用它的子类CompletableFuture去写业务。这里就不做介绍了。
本文涉及demo可以在百度云盘中获取:
链接:https://pan.baidu.com/s/1JBhTG5rSPNhm6mI2scaaRg
提取码:73e9
欢迎大家留言讨论!