本文将通过不同的场景,对多线程及线程池使用建议进行介绍,以下场景示例将以8核心CPU为例
1)任务数多但资源占用不大
场景解读:电商平台消息推送或短信通知,对于该场景来说需要被处理的消息对象内容简单所占用资源非常少,通常为百字节量级,但在高并发访问下,可能瞬间会产生大量的务数,而此类任务的处理通常效率较高,因此在重点在于控制并发线程数,不要因为大量的线程启用及线程的上下文频繁切换而导致内存使用率过高,CPU的内核态使用率过高等不良情况发生,通常可以在创建线程池时设置较长的任务队列,并以CPU内核数2-4倍(经验值)的值设置核心线程与扩展线程数,合理固定的线程数可以使得CPU的使用率更加平滑,如
BlockingQueue queue = newArrayBlockingQueue<>(4096);
ThreadPoolExecutor executor = newThreadPoolExecutor(16, 16, 0, TimeUnit.SECONDS, queue);
2)任务数不多但资源占用大
场景解读:在非社交流媒体的使用场景下,该情况多发生于文件流、长文本对象或批量数据加工的处理,如日志收集、图片流压缩或批量订单处理等场景,而在此类的场景下的单个资源处理,又往往会发生较大的资源消耗,因此为了使系统达到较强处理能力而同时又可以控制任务资源对内存过大的使用,通常可以在创建线程池时适当加大扩展线程数量,同时设置相对合理较小的任务队列长度,如此当遇到任务数突增情况下可以有更多的并发线程来应对,此外需要合理设置扩展线程空闲回收的等待时长以节省不必要的开销,如
BlockingQueue queue = newArrayBlockingQueue<>(512);
ThreadPoolExecutor executor = newThreadPoolExecutor(16, 64, 30, TimeUnit.SECONDS, queue);
3)极端场景情况
场景解读:如遇任务资源较大、任务数较多同时处理效率不高的场景下,首先需要考虑任务的产生发起需要限流,理论上讲为保障系统的可用性及稳定运行,任务的发起能力应当略小于任务处理能力,其次对于类似场景可以采用以时间换取空间的思想,充分利用系统计算资源,当遇到任务处理能力不足的情况下,任务发起方的作业将被阻塞,从而充分保护系统的资源开销边界,但可能会导致CPU核心态的使用率高,如
BlockingQueue queue = newSynchronousQueue<>();
ThreadPoolExecutor executor = newThreadPoolExecutor(64, 64, 0, TimeUnit.SECONDS, queue);