业务场景:已有的项目中,已经有很多地方用到了线程池,并且当时只定义了一个线程池,但是我要在项目中重新引入定义一个新的线程池做我的业务使用,那么此时就会出现问题
业务场景分析
查看项目中ExecutorService的引用处,有很多很多个
如果,直接加入后,项目直接无法启动(注入失败)代码具体举例
出现的原因解释:因为之前的写法都是使用的@Autowired注解,根据类型去匹配,发现有两个同类型不同名字的bean,并且只是都是对象接收(并非集合),所以注入失败
package com.lzq.learn.test.线程池兼容问题;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
*
* 线程池配置类
*
* @author LiuZhiQiang
*/
@Configuration
public class ThreadPoolConfig {
private static final Integer MAX_THREAD = 20;
// 已有的
@Bean
public ExecutorService buildThreadPool(){
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("test-queue-thread-%d").build();
return Executors.newFixedThreadPool(MAX_THREAD,threadFactory);
}
// 本次版本,新添加的
@Bean("newTaskThreadPool")
public ExecutorService newTaskThreadPool() {
int corePoolSize = 10;
int maxPoolSize = 20;
int queueCapacity = 20;
long keepAliveSeconds = 60;
return new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveSeconds,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(queueCapacity),
new ThreadFactory() {
private final AtomicInteger threadCount = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("newTaskThreadPool_" + threadCount.getAndIncrement());
return thread;
}
},
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
}
解决方案
要想解决启动失败并且不改动原先代码的情况下,解决办法也很简单。就是在原先的@Bean下方添加@Primary注解,表示该bean的优先级较高,可以单独注入单个对象
如图:
测试代码:
package com.lzq.learn.test.线程池兼容问题;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.ExecutorService;
/**
*
* @author LiuZhiQiang
*/
@RestController(value = "testPoolController")
@RequestMapping("testPool")
public class TestController {
@Autowired
private ExecutorService executorService1;
@Autowired
private ExecutorService executorService2;
@Autowired
private ExecutorService executorService3;
@Autowired
@Qualifier("newTaskThreadPool")
private ExecutorService executorService7;
@Autowired
private List<ExecutorService> executorServiceList;
@GetMapping
public void testPool() {
executorService7.execute(() -> {
System.out.println("executorService7 当前线程池的名字是 : " + Thread.currentThread().getName());
});
executorService1.execute(() -> {
System.out.println("executorService1 当前线程池的名字是 : " + Thread.currentThread().getName());
});
executorService2.execute(() -> {
System.out.println("executorService2 当前线程池的名字是 : " + Thread.currentThread().getName());
});
executorService3.execute(() -> {
System.out.println("executorService3 当前线程池的名字是 : " + Thread.currentThread().getName());
});
int index = 1;
for (ExecutorService executorService : executorServiceList) {
int finalIndex = index;
executorService.execute(() -> {
System.out.println("executorServiceList["+ finalIndex +"] 当前线程池的名字是 : " + Thread.currentThread().getName());
});
index++;
}
}
}
测试结果图片
图片1
此图为控制台打印结果,有图可知,我们可以根据bean的name来区分注入的是哪一个bean,IOC同时默认的注入的就是带有@Primary的老的bean,所以我们就无需去改动之前定义的地方,只增强我们的新的版本改动即可。
图2
此截图是断点调试,可以发现,我们虽然声明注入了多个相同类型的bean,但是他们的地址也只有两个,这也侧面说明了我们@Bean的单例。并且注入List的时候,会把所有的定义的bean都注入进来