在使用多线程时,频繁地创建和销毁线程会带来显著的性能开销。使用线程池可以避免这种开销,并提供了以下优点:
在线程池中,给线程命名是一项良好的实践,有助于追踪和排查问题。通过为每个线程设置有意义的名称,可以快速定位具体的任务或线程,并进行更有效的日志追踪,可以根据自己公司对线程池的规范来命名。
Java中的线程池通常使用ThreadPoolExecutor类来创建和管理。可通过ThreadFactory参数自定义创建线程的工厂,进而实现为线程设置名称。下面是一个示例:
public static void main(String[] args) {
// 工单线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
1, TimeUnit.MINUTES, // 线程空闲超时时间
new LinkedBlockingQueue<>(2000), // 等待队列
new ThreadFactory() {
private final AtomicInteger poolNumber = new AtomicInteger(1);
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final ThreadGroup group = new ThreadGroup("OrderThreadPool-" + poolNumber.getAndIncrement());
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, group.getName()+"-Thread-" + threadNumber.getAndIncrement());
return t;
}
}
);
for(int i=0;i<7;i++){
executor.execute(() -> {
System.out.println(Thread.currentThread().getName());
// 具体的任务代码
});
}
}
打印:
OrderThreadPool-1-Thread-1
OrderThreadPool-1-Thread-5
OrderThreadPool-1-Thread-4
OrderThreadPool-1-Thread-2
OrderThreadPool-1-Thread-3
OrderThreadPool-1-Thread-5
OrderThreadPool-1-Thread-1
Java并发库(JUC)提供了几个预定义的线程池,如Executors.newFixedThreadPool()、Executors.newSingleThreadExecutor()等。虽然这些线程池在简单场景下很方便,但在某些情况下,它们可能会引发性能问题。
这些预定义的线程池具有一些缺点:
因此,在实际开发中,尽量避免使用Executors自带的四个线程池,而是根据需求手动创建ThreadPoolExecutor,以便更好地管理和配置线程池。
在Spring Boot中,@Async注解基于动态代理,如果不指定要使用的线程池,会默认使用Spring Boot提供的线程池,并且没有等待队列的限制,这可能导致等待队列无限大导致OOM,因此需要声明一个线程池。
首先,在Spring配置类上添加@EnableAsync注解,以启用异步方法的支持:
@Configuration
@EnableAsync
public class AppConfig {
// 配置其他的Bean或组件
}
接下来,创建一个线程池的配置类,用来配置自定义的线程池:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
@Configuration
public class ExecutorConfig implements AsyncConfigurer {
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 核心线程数
executor.setMaxPoolSize(20); // 最大线程数
executor.setQueueCapacity(50); // 等待队列大小
executor.setThreadNamePrefix("MyAsyncThread-"); // 线程名前缀
executor.initialize();
return executor;
}
}
最后,在需要异步执行的方法上添加@Async注解,并指定要使用的线程池名称:
@Service
public class MyService {
@Async("taskExecutor")
public void asyncMethod() {
// 异步方法的具体实现
}
}
可以,一般项目中会用到多个线程池,根据不同业务定义不同线程池,可以通过 static 或者交给 spring 管理都是可以的。