一、背景

项目中已经存在若干个线程池,为了保证业务之间互不影响,不会因为某一个业务的线程使用率极高,导致其余业务无线程可用或处理时间过长,现需采用线程池隔离的思想。

二、具体代码实现

1. 自定义线程池配置类

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
* 线程池配置类
* @author saint
*/
@Configuration
@EnableAsync
@Slf4j
public class ThreadPoolConfig {

@Bean("multiGuaranteeExecutor")
public Executor multiGuaranteeExecutor() {
ThreadPoolTaskExecutor multiGuaranteeExecutor = new ThreadPoolTaskExecutor();
multiGuaranteeExecutor.setCorePoolSize(10);
multiGuaranteeExecutor.setMaxPoolSize(20);
multiGuaranteeExecutor.setQueueCapacity(200);
multiGuaranteeExecutor.setKeepAliveSeconds(30);
// 设置线程名称前缀,当程序出现性能问题时,使用jstack命令、JvisualVM、JProfile工具根据线程名称可以快速定位代码问题。
multiGuaranteeExecutor.setThreadNamePrefix("multi-guarantee-thread--");
// 使用自定义拒绝策略,保证重要数据不丢失
multiGuaranteeExecutor.setRejectedExecutionHandler(new CustomRejectedExecutionHandler());
return multiGuaranteeExecutor;
}

@Bean("simpleExecutor")
public Executor simpleExecutor() {
ThreadPoolTaskExecutor simpleExecutor = new ThreadPoolTaskExecutor();
simpleExecutor.setCorePoolSize(10);
simpleExecutor.setMaxPoolSize(20);
simpleExecutor.setQueueCapacity(200);
simpleExecutor.setKeepAliveSeconds(30);
simpleExecutor.setThreadNamePrefix("simple-thread--");
simpleExecutor.setRejectedExecutionHandler(new CustomRejectedExecutionHandler());
return simpleExecutor;
}

/**
* Customize thread factory
*/
private class CustomThreadFactory implements ThreadFactory {
private AtomicInteger count = new AtomicInteger(1);

@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
String threadName = "multi-guarantee-thread-" + count.getAndIncrement();
t.setName(threadName);
return t;
}
}


/**
* Customize rejected handler
*/
private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
// 一直尝试往队列中添加任务
// 此处通过打印日志、发MQ等操作保证数据不丢失;减少线程池的工作压力。
executor.getQueue().put(r);
log.info("multi-guarantee thread executed failure");
} catch (InterruptedException e) {
log.error("Exception in putting thread queue again, ", e);
}

}
}
}

2. 自定义线程池的使用

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
* @author xzhou.Saint
*/
@Slf4j
@Service
public class AsyncService {

@Async("multiGuaranteeExecutor")
public void one() throws Exception {
log.info("one");
TimeUnit.SECONDS.sleep(2);
log.info("one success");
}

@Async("multiGuaranteeExecutor")
public void two() throws Exception {
log.info("two");
TimeUnit.SECONDS.sleep(2);
log.info("two success");
}

@Async("simpleExecutor")
public void three() throws Exception {
log.info("three");
TimeUnit.SECONDS.sleep(2);
log.info("three success");
}
}

3. 测试类

@SpringBootTest
class ApplicationTests {

@Autowired
private AsyncService asyncService;

@Test
void contextLoads() throws Exception {
asyncService.one();
asyncService.two();
asyncService.three();

Thread.currentThread().join();
}

}

观察日志我们可以发现,两个线程池已经生效。