实习时被大佬问了一个问题,如何将一个foreach操作分别在不同线程中执行后同步聚集异步操作的结果,之后执行后面的操作以提升响应速度,当时上线问题还有一个没解决所以忙乱中只想出一个通过CountDownLatch(闭锁)的方法,之后仔细想了想至少有三种方法可以解决这个问题。
闭锁
第一种方式闭锁,不用多说,下面给出伪代码
public static void main(String[] args) throws Exception{
int threadPoolSize = 10;
ExecutorService executorService = Executors.newFixedThreadPool(10);
CountDownLatch countDownLatch = new CountDownLatch(threadPoolSize);
List<Object> aggregationResult = new ArrayList<>();
for (int i = 0; i < threadPoolSize; i++) {
executorService.execute(() -> {
// TODO 子结果计算
aggregationResult.add(子结果);
countDownLatch.countDown();
});
}
countDownLatch.await();
// 对聚合结果进行下一步操作
...
}
volatile类型的flag变量
第二中方式,使用线程安全的volatile类型的flag变量作为子任务计算是否完成的标志,在没有完成前在主线程忙等。
public static void main(String[] args) throws Exception{
int threadPoolSize = 10;
volatile int flag = threadPoolSize;
ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Object> aggregationResult = new ArrayList<>();
for (int i = 0; i < threadPoolSize; i++) {
executorService.execute(() -> {
// TODO 子结果计算
aggregationResult.add(子结果);
flag --;
});
}
while(flag > 0) {
}
// 对聚合结果进行下一步操作
...
}
FutureTask
第三种方式是通过FutureTask,调用get方法的线程在get方法返回之前将一直被阻塞。为了更加通用使用RxJava做了封装
聚合工具类
@Component
public class AggregationUtilBean<R, T> {
private ExecutorService executorService = Executors.newFixedThreadPool(16);
public List<R> aggregation(List<T> keys, Function<T, R> consumer) {
List<R> result = new ArrayList<>();
Observable.from(keys).map(e -> executorService.submit(() -> consumer.apply(e))).map(e -> {
try {
return e.get();
} catch (InterruptedException | ExecutionException e1) {
e1.printStackTrace();
return null;
}
}).subscribe(result::add);
return result;
}
}
测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class AggregationUtilTest {
@Autowired
private AggregationUtilBean<Integer, Integer> aggregationUtilBean;
@Autowired
private AggregationUtilBean<Student, Integer> aggregationUtilBean2;
@Test
public void aggregationUtilBeanTest() {
List<Integer> aggregation = aggregationUtilBean.aggregation(Arrays.asList(1, 2, 3), e -> 2 * e);
System.out.println("result");
aggregation.forEach(System.out::println);
List<Student> aggregation2 = aggregationUtilBean2.aggregation(Arrays.asList(18, 19, 20), Student::new);
aggregation2.forEach(System.out::println);
}
private class Student {
private String name;
private Integer age;
public Student(Integer age) {
this.name = "给我也整一个";
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
}
结果
result
2
4
6
Student{name='给我也整一个', age=18}
Student{name='给我也整一个', age=19}
Student{name='给我也整一个', age=20}