背景
最近项目中有很多使用线程池进行处理的地方,同时也碰到了几个问题比如线程池的个数该怎么评估,线程程的该怎么具体去使用,结合项目和实际场景得到一些理解
使用多线程的三种方式
设定多线程数量
通过指定核心和最大线程数大于1的方式来执行多个线程任务
适用场景:线程任务无需区分顺序,只需要有资源执行即可
new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MINUTES, queue, new ThreadFactoryBuilder().setNameFormat(threadKey).setUncaughtExceptionHandler((t, e) -> log.error("handler error: ", Thread.currentThread().getName(), e)).build(), new DiscardPolicy());
Map管理多个线程池方式
使用map管理多个线程池
适用场景:适合任务需要顺序执行的场景,通过任务的唯一键进行hash获取对应的线程池对象
public static final Map<String, ExecutorService> map= new ConcurrentHashMap<>();public static void execute(String key, Runnable runnable) {String threadKey = key;ExecutorService service = map.get(threadKey);if (service == null) {service = init(threadKey);tsReciveThreadMap.put(threadKey, service);}service.execute(runnable);}
本地异步队列
启动独立的线程,使用LinkedBlockingQueue
循环等待接收消息
适用场景:适合批量等待处理一批数据的场景,循环等待一定时间,批量存储数据
public Map<String, BlockingQueue> queue = new HashMap<>();public void init(ExecutorService executorService) {for (int i = 0; i < 5; i++) {LinkedBlockingQueue<T> blockingQueue = new LinkedBlockingQueue<>();String key = i;queue.put(key, blockingQueue);executorService.execute(() -> {while (true) {try {T take = blockingQueue.take();Map<String, T> data = new HashMap<>();data.put(take.getKey(), take);long current = System.currentTimeMillis();while (!blockingQueue.isEmpty() && data.size() < 100 && System.currentTimeMillis() - current < 1000) {T take1 = blockingQueue.poll();if (take1 != null) {data.put(take1.getKey(), take1);}}this.handleMessage(data);} catch (Exception e) {log.error("", e);}}});}}
线程参数配置
网上有一个比较广传的线程数设定公式
CPU 密集型的程序 - 核心数 + 1
I/O 密集型的程序 - 核心数 * 2
但是我们在设定线程数的时候,应该根据当前任务的具体逻辑来判断而不是使用网上这种通用公式。
以下有几个自己在项目中的实际体验
1.关注我们提交任务给线程池的流入速率
2.关注我们线程任务执行的流出速率
3.结合处理速率设定队列的大小
总结
上面列出了在实际项目中使用的3种线程池的方式,使用线程池要结合业务场景以及