之前写过一篇java线程池ThreadPoolExecutor使用无界队列LinkedBlockingQueue实现多线程简单记录了下ThreadPoolExecutor使用无界队列LinkedBlockingQueue实现多线程的用法。但是在实际应用中,有些并发量大的请求场景,直接如此用会被同时创建多个线程池,会有内存不够用的风险,所以可以考虑用单例模式来管理线程池的调用。
最简单的办法可以是把初始化的ThreadPoolExecutor用@Bean直接注入到springboot中,这样是单例的,不过这样就变成了公用线程池,用完不能shutdown(),会一直存在应用中占用一点内存,当然springboot也自带了线程池可以直接调用,但是因为是公用的所以配置不能根据不同业务灵活改变,所以必要时候还是自己写一个比较好。
对单例模式不太熟的话可以看下面两篇博文,讲的很详细了,一般实现方式有饿汉式、懒汉式、双重检测和静态内部类的方法实现单例,综合使用场景,我觉得静态内部类的方法最适合。
单例模式参考博文链接:https://www.cnblogs.com/jingpeipei/p/5771716.html
https://www.cnblogs.com/damsoft/p/6105122.html
下面就用静态内部类的方法创建一个单例来管理ThreadPoolExecutor的创建:
新建ExcutorServiceSingleton类
package com.ly.mp.project.config;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ExcutorServiceSingleton {
private int corePoolSize = 50;
private int maximumPoolSize = 500;
private TimeUnit unit = TimeUnit.MILLISECONDS;
private int capacity = 2000;
private ExecutorService executorPool;
public ExcutorServiceSingleton() {
super();
}
private static class SingletonContainer{
private static ExcutorServiceSingleton instance = new ExcutorServiceSingleton();
}
public static ExcutorServiceSingleton getInstance() {
return SingletonContainer.instance;
}
public ExecutorService getThreadPoolExecutor() {
if(null == executorPool || executorPool.isShutdown()) {
executorPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
0L, unit,
new LinkedBlockingQueue<Runnable>(capacity));
}
return executorPool;
}
}
业务测试方法:
@SuppressWarnings("rawtypes")
@Override
public ResponseVO test(Map<String, Integer> params) {
Integer size = params.get("size");
Map<String, Object> resultMap = new HashMap<String, Object>();
ExecutorService executorPool = ExcutorServiceSingleton.getInstance().getThreadPoolExecutor();
for(int i = 0; i < size; i++) {
executorPool = ExcutorServiceSingleton.getInstance().getThreadPoolExecutor();
@SuppressWarnings("unchecked")
Future future = executorPool.submit(new Callable(){
@Override
public Object call(){
try {
System.out.println(Thread.currentThread().getId() + " " + Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
});
System.out.println(i + "===");
resultMap.put(i+"", future);
}
executorPool.shutdown();
Map<String, Object> responseMap= new HashMap<String, Object>();
for(String key : resultMap.keySet()){
try {
responseMap.put(key, ((Future)resultMap.get(key)).get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// System.out.println(JSON.toJSONString(responseMap));
return new ResponseVO<>();
}
注意,这种自己创建的线程池用完之后要及时关闭,考虑到并发的情况,当线程池存在且没有被关闭时也可以同时处理其他并发的调用,shutdown之后再调用时会重新生成一个新的线程池继续接收处理请求。这样兼顾了占用内存和性能。测试时第一个请求size赋值1000,同时第二个请求size赋值为1,当size为1的请求处理完毕,线程池被关闭之后,第一个请求还没有处理完,剩余的请求会创建一个新的线程池继续处理剩余的请求,处理完之后会关闭线程池,这样用起来不同业务场景可以新建不同配置,比较灵活,用完即关也比较节省内存。
网友评论