线程池工具类几种实现
线程池参数:核心线程数设置,根据生产环境平时QPS,任务处理能力决定,但也不能绝对参照这一算法。也与服务器整体处理能力,配置有关。
如:QPS是10,处理任务时间2S,核心线程数至少应该设置为20。也就是,10个任务需要总时长20S完成。那至少需要20个线程同时处理,粗略算法,其他因素影响需要留出冗余。
还有一种核心线程数 设置公式参见:https://www.cnblogs.com/warehouse/p/10810338.html
其结论:
IO密集型 = 2Ncpu(可以测试后自己控制大小,2Ncpu一般没问题)(常出现于线程中:数据库数据交互、文件上传下载、网络数据传输等等)
计算密集型 = Ncpu(常出现于线程中:复杂算法)+ 1
混合型(有逻辑计算如:程序加解密,视频解密等,也有IO如:RPC远程调用、数据库访问) = ((线程等待时间 + 线程 CPU 时间) / 线程 CPU 时间 ) * CPU 核数
一 线程池工具类
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @Description 线程池工具类
*/
public class ThreadPoolUtil {
/**
* 核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量
*/
private static final int SIZE_CORE_POOL = 5;
/**
* 线程池维护线程的最大数量
*/
private static final int SIZE_MAX_POOL = 10;
/**
* 线程池维护线程所允许的空闲时间
*/
private static final long ALIVE_TIME = 2000;
/**
* 线程缓冲队列
*/
private static BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(100);
private static ThreadPoolExecutor pool = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, ALIVE_TIME, TimeUnit.MILLISECONDS, bqueue, new ThreadPoolExecutor.CallerRunsPolicy());
static {
pool.prestartAllCoreThreads();
}
public static ThreadPoolExecutor getPool() {
return pool;
}
}
测试类
import com.dashuai.cloud.consulconsumer.util.ThreadPoolUtil;
public class TestUtil {
public static void main(String[] args) {
ThreadPoolUtil.getPool().execute(new Runnable() {
@Override
public void run() {
System.out.println("线程池调用");
}
});
}
}
二 线程池支持多线程返回结果
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.DisposableBean;
/**
* ClassName:CommenThreadPoolUtil <br/>
* Function:线程池公共入口处理类. <br/>
*
*/
@Service
public class CommonThreadPoolUtil implements DisposableBean{
// 核心线程数(默认初始化为10)
private int cacheCorePoolSize = 8;
// 核心线程控制的最大数目
private int maxCorePoolSize = 160;
// 队列等待线程数阈值
private int blockingQueueWaitSize = 16;
// 核心线程数自动调整的增量幅度
private int incrementCorePoolSize = 4;
// 初始化线程对象ThreadLocal,重写initialValue(),保证ThreadLocal首次执行get方法时不会null异常
private ThreadLocal<List<Future<?>>> threadlocal = new ThreadLocal<List<Future<?>>>() {
protected List<Future<?>> initialValue() {
return new ArrayList<Future<?>>();
}
};
// 初始化线程池
private MyselfThreadPoolExecutor ThreadPool = new MyselfThreadPoolExecutor(cacheCorePoolSize, cacheCorePoolSize, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
/**
*
* dealTask:(线程池执行操作-包含每个进程返回结果). <br/>
* 1、运用场景:例如,需要同时校验很多不同的逻辑,依赖于获取校验结果响应给用户; 2、具体实现java类:implements
* 的Callable接口,重写call方法即可,支持返回值
*
* @author
* @param callable
* @return
*/
public Map<String, Object> dealTask(Callable<?> callable) {
try {
// 动态更改核心线程数大小
dynamicTuningPoolSize();
// 执行线程业务逻辑及获取返回结果
Future<?> result = ThreadPool.submit(callable);
// 获取当前进程的局部变量
List<Future<?>> threadLocalResult = threadlocal.get();
// 叠加主进程对应的多个进程处理结果
threadLocalResult.add(result);
// 设置最新的threadLocal变量到当前主进程
threadlocal.set(threadLocalResult);
} catch (Exception e) {
e.printStackTrace();
return errorResp("线程池发生异常-Future", null);
}
return successResp(null);
}
/**
*
* dealTask:(线程池执行操作-不包含每个进程返回结果). <br/>
* 1、运用场景:例如,不依赖于响应给用户执行结果的业务逻辑 ; 2、具体实现java类:implements
* 的Runnable接口,重写run方法,没有返回值
*
* @author
* @param runnable
* @return
*/
public Map<String, Object> dealTask(Runnable runnable) {
try {
// 动态更改核心线程数大小
dynamicTuningPoolSize();
// 执行线程业务逻辑
ThreadPool.execute(runnable);
} catch (Exception e) {
e.printStackTrace();
return errorResp("线程池发生异常", null);
}
return successResp(null);
}
/**
* obtainTaskFuture:(获取线程池执行结果:此为阻塞线程,即所有线程都执行完成才能获取结果,故应将执行时间稍长的业务逻辑先执行,
* 减少等待时间). <br/>
* 此方法只能调用一次,即调用之后清除ThreadLocal变量,以便于同一进程再次调用线程池获取最新的执行结果以及释放内存, 防止内存泄露
*
* @author
* @return
*/
public Map<String, Object> obtainTaskFuture() {
List<Future<?>> threadLocalResult = null;
try {
// 获取当前进程变量
threadLocalResult = threadlocal.get();
if (threadLocalResult == null || threadLocalResult.size() == 0) {
return errorResp("获取线程池执行结果为空", null);
} else {
return successResp(threadLocalResult);
}
} catch (Exception e) {
return errorResp("获取线程池执行结果发生异常:" + e.getMessage(), null);
} finally {
// 1、释放内存;2、防止主进程再次调用线程池方法时对结果互有影响。
threadlocal.remove();
}
}
/**
*
* dynamicTuningPoolSize:(动态改变核心线程数). <br/>
*
* @author
* @return
*/
private void dynamicTuningPoolSize() {
// 队列等待任务数(此为近似值,故采用>=判断)
int queueSize = ThreadPool.getQueueSize();
// 动态更改核心线程数大小
if (queueSize >= blockingQueueWaitSize) {
// 核心线程数小于设定的最大线程数才会自动扩展线程数
if (cacheCorePoolSize <= maxCorePoolSize) {
// 原有核心线程数
int corePoolSize = ThreadPool.getCorePoolSize();
// 将要累积的核心线程数
int currentcorePoolSize = corePoolSize + incrementCorePoolSize;
ThreadPool.setCorePoolSize(currentcorePoolSize);
ThreadPool.setMaximumPoolSize(currentcorePoolSize);
cacheCorePoolSize = currentcorePoolSize;
System.out.println("动态改变线程池大小====原核心线程池数目为:" + corePoolSize + ";现累加为:" + currentcorePoolSize);
} else {
System.out.println("动态改变线程池大小====核心线程池数目已累加为:" + cacheCorePoolSize + ";不会继续无限增加");
}
} else {
// 缩容
if (queueSize == 0 && cacheCorePoolSize >= CORE_POOL_SIZE) {
// 原有核心线程数
int corePoolSize = ThreadPool.getCorePoolSize();
// 将要累积的核心线程数
int currentcorePoolSize = corePoolSize - incrementCorePoolSize;
if (currentcorePoolSize <= CORE_POOL_SIZE) {
currentcorePoolSize = CORE_POOL_SIZE;
}
ThreadPool.setCorePoolSize(currentcorePoolSize);
ThreadPool.setMaximumPoolSize(currentcorePoolSize);
cacheCorePoolSize = currentcorePoolSize;
}
}
}
/**
* 获取核心线程数 getCacheCorePoolSize:(). <br/>
*
* @author
* @return
*/
public int getCacheCorePoolSize() {
return ThreadPool.getCorePoolSize();
}
/**
* 设置核心线程数 setCacheCorePoolSize:(). <br/>
*
* @author
* @param cacheCorePoolSize
*/
public void setCacheCorePoolSize(int cacheCorePoolSize) {
ThreadPool.setCorePoolSize(cacheCorePoolSize);
ThreadPool.setMaximumPoolSize(cacheCorePoolSize);
this.cacheCorePoolSize = cacheCorePoolSize;
}
/**
*
* successResp:(正确响应信息). <br/>
*
* @author
* @param data
* @return
*/
private Map<String, Object> successResp(Object data) {
Map<String, Object> result = new HashMap<String, Object>();
result.put("status", "0");
result.put("data", data);
return result;
}
/**
*
* errorResp:(错误响应信息). <br/>
*
* @author
* @param errorMsg
* @param data
* @return
*/
public Map<String, Object> errorResp(String errorMsg, Object data) {
Map<String, Object> result = new HashMap<String, Object>();
result.put("status", "1");
result.put("msg", errorMsg);
result.put("data", data);
return result;
}
@Override
public void destroy() throws Exception {
ThreadPool.shutdown();
logger.info("线程池销毁");
}
}
创建线程池类
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyselfThreadPoolExecutor extends ThreadPoolExecutor {
// 初始化父类构造函数及startTime
public MyselfThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
// 按过去执行已提交任务的顺序发起一个有序的关闭,但是不接受新任务(已执行的任务不会停止)
@Override
public void shutdown() {
super.shutdown();
}
// 尝试停止所有的活动执行任务、暂停等待任务的处理,并返回等待执行的任务列表。在从此方法返回的任务队列中排空(移除)这些任务。并不保证能够停止正在处理的活动执行任务,但是会尽力尝试。
@Override
public List<Runnable> shutdownNow() {
return super.shutdownNow();
}
// 在执行给定线程中的给定 Runnable 之前调用的方法.可用于重新初始化ThreadLocals或者执行日志记录。
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
}
// 基于完成执行给定 Runnable 所调用的方法
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
try {
// Future<?> result = (Future<?>) r;
// "任务结果:" result.get();
} catch (Exception e) {
}
}
/**
*
* getQueueSize:(已执行的任务数). <br/>
*
* @author
* @return
*/
@Override
public long getCompletedTaskCount() {
return super.getCompletedTaskCount();
}
/**
*
* getQueueSize:(正在运行的任务数). <br/>
*
* @author
* @return
*/
@Override
public int getActiveCount() {
return super.getActiveCount();
}
/**
*
* getQueueSize:(队列等待任务数). <br/>
*
* @author
* @return
*/
public int getQueueSize() {
return getQueue().size();
}
}
测试类
public class TestUtil {
public static void main(String[] args) {
CommonThreadPoolUtil poolUtil = new CommonThreadPoolUtil();
poolUtil.dealTask(new Runnable() {
@Override
public void run() {
System.out.println("线程池调用");
}
});
poolUtil.dealTask(new Callable<HashMap<String, Object>>() {
@Override
public HashMap<String, Object> call() {
System.out.println("线程池调用");
return new HashMap<String, Object>();
}
}
Map<String, Object> result = poolUtil.obtainTaskFuture();
List<Future<HashMap<String, Object>>> list = (List<Future<HashMap<String, Object>>>) result.get("data");
for(int j = 0; j < list.size(); j++){
Future<HashMap<String, Object>> future = list.get(j);
}
}
}
三 jdk1.5之后提供工具类 Executors
工具类Executors面提供了一些静态工厂方法,生成一些常用的线程池,如下所示:
-
newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制(Interger. MAX_VALUE),线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
-
newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
-
newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。 如果某个线程因为执行异常而结束,那么线程池会补充一个新线程,这个是非线程池做不到的,一旦出现异常接下来的任务则被终止。
-
newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
总结:除了newScheduledThreadPool的内部实现特殊一点之外,其它线程池内部都是基于 ThreadPoolExecutor 类(Executor的子类)实现的。
实现:
public class TestUtil {
public static void main(String[] args) {
ScheduledExecutorService scheduExec = Executors.newScheduledThreadPool(10);
scheduExec.schedule(new Runnable() {
@SuppressWarnings("static-access")
@Override
public void run() {
System.out.println("20秒后处理");
}
}, 20, TimeUnit.SECONDS);
}
}
周期性定时任务20秒后执行
四、线程池组
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Service("dispatchExecutorsPool")
public class DispatchExecutorsPool implements DisposableBean {
private int default_corePoolSize = 4;
private int default_maximumPoolSize = 500;
private int default_keepAliveTime = 300 * 1000;
private static final Logger logger = LoggerFactory.getLogger(DispatchExecutorsPool.class);
//初始化线程池
private ThreadPoolExecutor orderThreadPool = null;
private ThreadPoolExecutor productThreadPool = null;
@Value("${executor_core_size}")
private String executor_core_size;
@Value("${executor_max_size}")
private String executor_max_size;
@Value("${executor_alive_time}")
private String executor_alive_time;
/**
* 线程池集合
*/
List<ThreadPoolExecutor> executorList = new ArrayList<>();
@PostConstruct
public void init() {
orderThreadPool = generateOrderExecutor(executor_core_size, executor_max_size, executor_alive_time);
productThreadPool = generatePorductExecutor(executor_core_size, executor_max_size, executor_alive_time);
}
/**
* @Description: 订单拉取线程池
* @Date: 2021/8/9 14:23
* @Param: [coolSize, maxSize, keepAliveTime]
* @Return: java.util.concurrent.ThreadPoolExecutor
*/
private ThreadPoolExecutor generateOrderExecutor(String coolSize, String maxSize, String keepAliveTime) {
int configPoolSize = Integer.valueOf(coolSize);
int configMaxSize = Integer.valueOf(maxSize);
int configKeepAliveTime = Integer.valueOf(keepAliveTime);
ThreadPoolExecutor executor = new ThreadPoolExecutor((configPoolSize == 0 ? default_corePoolSize : configPoolSize),
(configMaxSize == 0 ? default_maximumPoolSize : configMaxSize),
(configKeepAliveTime == 0 ? default_keepAliveTime : configKeepAliveTime),
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
//使用hook在spring close的同时停止线程池shutdown之后的new submit task会走threadPool.setRejectedExecutionHandler
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
logger.error("DispatchExecutorsPool.rejectedExecution:" + executor.toString());
executor.awaitTermination(15, TimeUnit.SECONDS);
} catch (InterruptedException e) {
executor.getQueue().size();
logger.error("executor_drg.awaitTermination.error", e);
}
}
}
});
executorList.add(executor);
return executor;
}
/**
* @Description: 获取线程池
* @Date: 2021/8/9 14:24
* @Param: [coolSize, maxSize, keepAliveTime]
* @Return: java.util.concurrent.ThreadPoolExecutor
*/
private ThreadPoolExecutor generatePorductExecutor(String coolSize, String maxSize, String keepAliveTime) {
int configPoolSize = Integer.valueOf(coolSize);
int configMaxSize = Integer.valueOf(maxSize);
int configKeepAliveTime = Integer.valueOf(keepAliveTime);
ThreadPoolExecutor executor = new ThreadPoolExecutor((configPoolSize == 0 ? default_corePoolSize : configPoolSize),
(configMaxSize == 0 ? default_maximumPoolSize : configMaxSize),
(configKeepAliveTime == 0 ? default_keepAliveTime : configKeepAliveTime),
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
executor.setRejectedExecutionHandler(new RejectedExecutionHandler() {
//使用hook在spring close的同时停止线程池shutdown之后的new submit task会走threadPool.setRejectedExecutionHandler
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (!executor.isShutdown()) {
try {
logger.error("DispatchExecutorsPool.rejectedExecution:" + executor.toString());
executor.awaitTermination(15, TimeUnit.SECONDS);
} catch (InterruptedException e) {
executor.getQueue().size();
logger.error("executor_drg.awaitTermination.error", e);
}
}
}
});
executorList.add(executor);
return executor;
}
@Override
public void destroy() throws Exception {
for (ThreadPoolExecutor threadPoolExecutor : executorList) {
threadPoolExecutor.shutdown();
}
}
/**
* @Description: 获取订单线程池
* @Date: 2021/8/9 14:25
* @Param:
* @Return:
*/
public ThreadPoolExecutor getOrderThreadPool() {
return orderThreadPool;
}
/**
* @Description: 产品线程池
* @Date: 2021/8/9 14:26
* @Param: []
* @Return: java.util.concurrent.ThreadPoolExecutor
*/
public ThreadPoolExecutor getProductThreadPool() {
return productThreadPool;
}
}