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