一、应用场景
在实际工作中经常会遇到某些很复杂的逻辑,在业务和代码层面对返回速度的优化已经达到瓶颈,但时现在整个接口的返回速度还是不太令人满意,这个时候就可以考虑将部分没有强关联的业务改造成多线程,这样多个业务可以在多线程里面并发去处理,最后拿到返回值后再继续往下走,可以一定程度上加快接口的返回速度
二、代码模拟
1、首先我们模拟一下正常的场景
- 正常的场景,比如下面的创建user就算是业务逻辑,每个方法都要耗时1秒钟
private User createUser2(int i) throws InterruptedException { Thread.sleep(3000); User user = new User(); user.setAge(i); return user; } - 创建10个user
public void create() throws InterruptedException { System.out.println("开始"); long start = System.currentTimeMillis(); for (int i = 0; i < 10; i++) { createUser(i); } System.out.println("结束,耗时:" + (System.currentTimeMillis()-start)); } - 日志打印,基本上就是10秒,这个没啥好说的
开始
结束,耗时:10004
2、使用多线程去创建
- 多线程有多种实现方式,这里我们先用实现Runable接口的方式
public class UserRun1 implements Callable<User> {
private int age;
public UserRun1(int age) {
this.age = age;
}
@Override
public User call() throws Exception {
Thread.sleep(1000);
User user = new User();
user.setAge(age);
return user;
}
}
- 使用多线程去创建
public void create1() {
System.out.println("多线程开始");
long start = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(10) ;
List<User> userList = new ArrayList<>(10);
List<Future> futureList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int j = i;
Future<User> submit = executor.submit(new UserRun1(j));
futureList.add(submit);
}
System.out.println("futureList==" + futureList.size());
for (Future future : futureList) {
try {
User user = (User) future.get();
userList.add(user);
} catch (Exception e) {
log.error("e.printStackTrace()");
}
}
userList.stream().map(User::getAge).sorted().forEach(age -> System.out.print(age));
System.out.println("结束,耗时:" + (System.currentTimeMillis() - start));
System.out.println(userList.stream().map(User::getAge).filter(item -> item != null).count());
- 返回结果,因为我开的线程数量和实际业务数量一致,所以几乎就是单个业务的执行时间就是最后的返回时间,速度直接快了10倍
多线程开始
futureList==10
0123456789结束,耗时:1045
10
三、注意点
在多线程里面最需要注意的就是线程安全的问题了,比如在使用集合时,如果用下面这种方式去写就会有问题
- 在多线程中使用集合去添加元素
public void create1() throws Exception {
System.out.println("开始");
long start = System.currentTimeMillis();
ExecutorService executor = Executors.newFixedThreadPool(10) ;
List<User> userList = new ArrayList<>(10);
List<Future> futureList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int j = i;
Future submit = executor.submit(() -> {
try {
createUser(userList,j);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
futureList.add(submit);
}
futureList.stream().forEach(item -> {
try {
item.get();
} catch (Exception e) {
e.printStackTrace();
}
});
userList.stream().map(User::getAge).sorted().forEach(age -> System.out.print(age));
System.out.println("结束,耗时:" + (System.currentTimeMillis() - start));
Thread.sleep(3000);
userList.stream().map(User::getAge).sorted().forEach(age -> System.out.print(age));
}
private void createUser(List<User> userList,int i) throws InterruptedException {
Thread.sleep(1000);
User user = new User();
user.setAge(i);
userList.add(user);
}
- 返回结果,丢了8,这是因为createUser这个方法里面使用了userList.add去添加元素,ArrayList是线程不安全的,在多线程环境下有可能刚好两个下标同时添加,所以后面覆盖了前面的,正确的做法可以将List集合换成线程安全的Vector,或者用第一种避免操作集合的方式
开始
012345679结束,耗时:1037
012345679

本文探讨了在面临复杂逻辑且常规优化无法满足性能需求时,如何通过引入多线程来加速接口响应。通过代码模拟,展示了多线程在创建用户场景中的应用,以及在多线程环境下需要注意的线程安全问题。

985

被折叠的 条评论
为什么被折叠?



