场景:并发处理任务时,任务总数远大于线程池最大线程数,并且每一个任务内部还有并发处理的子任务,这种情况下,很容易导致线程死锁的情况,原因相信大家都清楚。故开发了该工具类。其中优缺点也很明显,但在业务处理中很实用。望大家多提提意见,完善该工具类。
/**
* @Description:
* @Author: huyk
* @CreateTime: 2023-12-06 11:27
*/
@Slf4j
public class ThreadUtil {
static {
executorService = ThreadPool.COMMON_POOL;
}
private static ExecutorService executorService;
private String groupId;
private CountDownLatch countDownLatch;
private ThreadUtil (@NonNull String groupId, int taskCount) {
ThreadPool.THREAD_GROUP_MAP.putIfAbsent(groupId,new ArrayBlockingQueue<>(ThreadPool.SINGLE_GROUP_TASK_THRESHOLD));
this.groupId = groupId;
this.countDownLatch = new CountDownLatch(taskCount);
}
public static ThreadUtil createLimiter(@NonNull String groupId, int taskCount){
return new ThreadUtil (groupId,taskCount);
}
public ThreadUtil subTask(@NonNull Runnable runnable){
ArrayBlockingQueue<ITask> subTaskQueue = ThreadPool.THREAD_GROUP_MAP.get(groupId);
ITask iTask = new ITask(groupId,runnable,countDownLatch);
try {
subTaskQueue.put(iTask);
}
catch (InterruptedException ie) {
log.error("[Thread-Limiter:subTask] InterruptedException:",ie);
}
boolean subSuccess = false;
do {
try{
CompletableFuture.runAsync(iTask,executorService);
subSuccess = true;
}
catch (RejectedExecutionException re){
rejectedExecutionSleep();
log.error("[Thread-Limiter:subTask] RejectedExecutionException:",re);
}
}
while (!subSuccess);
return this;
}
public void await(){
try {
countDownLatch.await();
}
catch (InterruptedException e) {
log.error("[Thread-Limiter:await] InterruptedException:", e);
}
}
private static void rejectedExecutionSleep(){
try {
Thread.sleep(ThreadPool.FAIL_PERIOD_MS);
}
catch (InterruptedException e) {
log.error("[Thread-Limiter:RejectedExecutionSleep] InterruptedException:",e);
}
}
private static class ITask implements Runnable{
private Runnable task;
private ArrayBlockingQueue<ITask> subTaskQueue;
private CountDownLatch countDownLatch;
public ITask(String groupId,
Runnable task,
CountDownLatch countDownLatch) {
this.task = task;
this.countDownLatch = countDownLatch;
this.subTaskQueue = ThreadPool.THREAD_GROUP_MAP.get(groupId);
}
@Override
public void run() {
try{
task.run();
}
catch (Exception e){
log.error("[Thread-Limiter:ITask] task.run():", e);
}
finally {
subTaskQueue.poll();
countDownLatch.countDown();
}
}
}
public static void runAsyncAndGet(@NonNull Runnable... runnables){
runAsyncAndGet(Arrays.asList(runnables));
}
public static void runAsyncAndGet(@NonNull List<Runnable> runnableList){
CompletableFuture[] completableFutures = runAsync(executorService, runnableList);
if(null != completableFutures){
try {
CompletableFuture.allOf(completableFutures).get();
}
catch (Exception e) {
log.error("[Thread-Limiter:runAsyncAndGet] error:", e);
}
}
}
public static CompletableFuture[] runAsync(@NonNull ExecutorService executor, @NonNull Runnable... runnables){
return runAsync(executor,Arrays.asList(runnables));
}
public static CompletableFuture[] runAsync(@NonNull ExecutorService executor, @NonNull List<Runnable> runnableList){
if(CollectionUtil.isNotEmpty(runnableList)){
CompletableFuture[] completableFutures = new CompletableFuture[runnableList.size()];
for (int i = 0; i < runnableList.size(); i++) {
boolean exeSuccess = false;
do {
try {
completableFutures[i] = CompletableFuture.runAsync(runnableList.get(i), executor);
exeSuccess = true;
}
catch (RejectedExecutionException re){
rejectedExecutionSleep();
log.error("[Thread-Limiter:runAsync] RejectedExecutionException:", re);
}
}
while (!exeSuccess);
}
return completableFutures;
}
return null;
}
private static class ThreadPool{
private static final String NEW_NAMED_THREAD_PREFIX = "wms-pool-thread-";
private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = AVAILABLE_PROCESSORS;
private static final int MAX_POOL_SIZE = CORE_POOL_SIZE + 5;
private static final int SINGLE_GROUP_TASK_THRESHOLD = CORE_POOL_SIZE * 2 / 5;
private static final long FAIL_PERIOD_MS = 20;
private static final ExecutorService COMMON_POOL =
ExecutorBuilder.create().setCorePoolSize(AVAILABLE_PROCESSORS)
.setMaxPoolSize(MAX_POOL_SIZE)
.setThreadFactory(ThreadUtil.newNamedThreadFactory(NEW_NAMED_THREAD_PREFIX, Boolean.FALSE))
.useSynchronousQueue()
.build();
private static final ConcurrentHashMap<String, ArrayBlockingQueue<ITask>> THREAD_GROUP_MAP = new ConcurrentHashMap<>();
}
}