使用线程池的益处
1、降低资源消耗;
线程是操作系统中比较稀缺的资源,大量创建线程池,不仅消耗系统资源,还会导致系统稳定性降低,在JVM中,最终导致OOM发生。通过使用线程池,限制线程数目的创建,可重复利用已创建的线程。
2、提高响应速度;
线程池可以复用已创建好的线程,不必每次任务到来就创建新的线程;而且线程池刚初始化时,可以预热线程池资源,通过
java.util.concurrent.ThreadPoolExecutor#prestartAllCoreThreads
提前创建核心线程池。
3、提高系统稳定性;
不同的业务使用不同的线程池隔离,可以提高系统的稳定性,而不用担心业务之间相互影响。
IO密集型任务如何确定线程数目
IO密集型任务对CPU的使用率比较低,IO处理时间稍长,IO阻塞期间导致线程空余,所以通常线程数目较多,一般为CPU核心数目的两倍。
java.lang.Runtime#availableProcessors * 2
CPU密集型任务如何确定线程数目
CPU密集型任务也叫计算密集型任务,即需要大量计算而非常消耗CPU资源的任务。要高效地利用CPU,就必须让每个CPU核都忙碌起来,线程数目应当为CPU的核心数目。
java.lang.Runtime#availableProcessors
线程数目如果多于CPU核心数目,需要操作系统频繁切换线程上下文,导致性能下降。
混合型任务如何确定线程数目
混合型任务即少量消耗CPU,又大量消耗IO的任务。一般我们的微服务系统就属于这种。
业界比较成熟的估算公式:
最佳线程数目 = (线程等待时间 / 线程CPU耗时时间 + 1) * CPU核心数目
从上面的公式可以得出:等待时间所占比例越高,就需要更多线程数;CPU耗时所占比例越高,就需要越少线程数;
当然上面的公式只是一个理论值,线程数目多了反而导致系统不稳定。生产环境我们必须时刻记得调整线程数目,以满足业务需求。
java提供的
java.util.concurrent.ThreadPoolExecutor
可以动态调整核心线程数和最大线程数,但是队列好像不支持动态调整,需要我们自己实现。
小结
线程是操作系统中比较稀缺的资源,大量创建线程池,不仅消耗系统资源,还会导致系统稳定性降低,所以需要根据任务类型的不同设置合理的线程数目。由于Java中协程还没出现,可以考虑使用go语言中原生支持的协程,去实现一些高性能的服务。