问题:
采用while(true)方式阻塞主线程,等待多个子线程执行完毕的方案。在系统负载升高时,存在while循环占用过多时间片,导致CPU被打满风险。
问题代码示例:
final long start = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - start >= 5 * MilliSecond.SECOND) {
break;
}
if (futureList.parallelStream().allMatch(Future::isDone)) {
break;
}
}
/**
* 等待全部线程完成(超时时间30s)
* @param futureList List<Future<Exception>>:future中装载线程内抛出的异常,线程正常结束则需要装载null
*/
public static void waitComplete(List<Future<Exception>> futureList) throws Exception {
final long start = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - start >= 30 * MilliSecond.SECOND) {
break;
}
if (futureList.stream().allMatch(Future::isDone)) {
for (Future<Exception> future : futureList) {
if (future.get() != null) {
throw future.get();
}
}
break;
}
}
}
解决方案:
建议使用java8提供的CompletableFuture,配合allof和join方法,阻塞主线程。
完整示例代码如下:
package com.mark.utils.thread;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Supplier;
/**
* 线程相关工具类
* @author mark
* @date 2022/9/8 10:58
*/
@Component
public class ThreadUtils {
@Resource
private ThreadPoolTaskExecutor commonTaskExecutor;
@Resource
private ThreadPoolTaskExecutor expensiveTaskExecutor;
/**
* 使用commonTaskExecutor线程池,开启新线程执行异步任务,新线程无返回值
*/
public CompletableFuture<Void> commonTaskExecutorRun(Runnable runnable) {
return CompletableFuture.runAsync(runnable, commonTaskExecutor);
}
/**
* 使用commonTaskExecutor线程池,开启新线程执行异步任务,新线程有返回值
*/
public <U> CompletableFuture<U> commonTaskExecutorSupply(Supplier<U> supplier) {
return CompletableFuture.supplyAsync(supplier, commonTaskExecutor);
}
/**
* 使用expensiveTaskExecutor线程池,开启新线程执行异步任务,新线程无返回值
*/
public CompletableFuture<Void> expensiveTaskExecutorRun(Runnable runnable) {
return CompletableFuture.runAsync(runnable, expensiveTaskExecutor);
}
/**
* 使用expensiveTaskExecutor线程池,开启新线程执行异步任务,新线程有返回值
*/
public <U> CompletableFuture<U> expensiveTaskExecutorSupply(Supplier<U> supplier) {
return CompletableFuture.supplyAsync(supplier, expensiveTaskExecutor);
}
/**
* 阻塞主线程,等待全部future执行完毕
*/
public static <U> void waitComplete(List<CompletableFuture<U>> futureList) {
if (CollectionUtils.isEmpty(futureList)) {
return;
}
CompletableFuture
.allOf(futureList.toArray(new CompletableFuture[0]))
.join();
}
/**
* 阻塞主线程,等待全部future执行完毕,线程返回的异常会被抛出
* @param futureList List<CompletableFuture<Exception>>:future中装载线程内抛出的异常,线程正常结束则需要装载null
*/
public static void waitCompleteThrow(List<CompletableFuture<Exception>> futureList) throws Exception {
waitComplete(futureList);
for (CompletableFuture<Exception> future : futureList) {
if (future.get() != null) {
throw future.get();
}
}
}
}
CompletableFuture API:
-
runAsync(Runnable runnable, Executor executor)
-
返回一个新的不携带返回值的CompletableFuture,使用指定的线程池执行任务。
-
-
supplyAsync(Supplier supplier, Executor executor)
- 返回一个新的携带返回值的CompletableFuture,使用指定的线程池执行任务。
-
allOf(CompletableFuture<?>… cfs)
-
返回一个新的CompletableFuture,当入参中所有CompletableFuture都执行完成后,它也会执行完成。
-
多个CompletableFuture独立执行,某个CompletableFuture抛出异常不会干扰其他。
-
-
get() throw InterruptedException, ExecutionException
-
阻塞当前线程,直到全部CompletableFuture执行完成,并拿到执行结果。
-
CompletableFuture执行过程中的异常会被抛出,进而结束掉主线程。此外,多个异常只有一个会被抛出。
-
该方法抛出checked异常。
-
-
join()
- 阻塞当前线程,直到全部CompletableFuture执行完成,并拿到执行结果。
- CompletableFuture执行过程中的异常会被抛出,进而结束掉主线程。此外,多个异常只有一个会被抛出。
- 该方法返回unchecked异常,这是该方法和get()方法的主要区别。