CPU密集型和IO密集型与CPU内核之间的关系
一、CPU密集型
-
介绍
CPU密集型,也叫计算密集型,是指需要大量CPU计算资源,例如大量的数学运算、图像处理、加密解密等。这种类型的任务主要依赖于CPU的计算能力,会占用大量的CPU时间片,使得CPU内核在执行任务时负载较重。CPU密集型任务在执行过程中主要消耗CPU资源,而对IO操作的需求相对较少。
-
与CPU内核的关系
CPU密集型任务与CPU内核之间的关系在于任务的执行方式和CPU资源的利用。CPU密集型任务需要大量的CPU计算资源,通常会占用大量的CPU时间片,使得CPU内核在执行任务时负载较重。在多核处理器系统中,如果任务可以充分利用所有的CPU内核,那么整体的计算性能会得到提升。
在自定义线程池中可以设置 核心线程数=CPU内核数+1,在处理CPU密集型任务时,通常希望充分利用系统中的所有CPU内核,同时避免线程频繁地创建和销毁带来的开销。根据经验,将核心线程数设置为CPU核数加1可以在大多数情况下达到比较好的性能表现。
设置核心线程数为CPU核数加1的原因是为了确保在系统负载较重的情况下,仍然有一个空闲的线程可以立即执行任务,从而避免因线程创建和销毁带来的性能损失。这样可以更好地利用系统的计算资源,提高CPU密集型任务的执行效率。
-
代码测试
public class CPUAndIOTest { public static void main(String[] args) { int availableProcessors = Runtime.getRuntime().availableProcessors(); System.out.println("CPU内核数:" + availableProcessors); // 测试CPU密集型任务 testCPUIntensiveTask(availableProcessors); } private static void testCPUIntensiveTask(int availableProcessors){ long startTime = System.currentTimeMillis(); int taskCount = 100; // 任务数量 int corePoolSize = availableProcessors + 1; ExecutorService executor = Executors.newFixedThreadPool(corePoolSize); for (int i = 0; i < corePoolSize; i++) { try { Thread.sleep(50); } catch (InterruptedException e) { throw new RuntimeException(e); } executor.execute(() -> { // 模拟CPU密集型任务 double result = 0; for (int j = 0; j < taskCount/corePoolSize; j++) { try { Thread.sleep(50); } catch (InterruptedException e) { throw new RuntimeException(e); } result += Math.random(); } }); } executor.shutdown(); while (!executor.isTerminated()) { } long endTime = System.currentTimeMillis(); System.out.println("CPU密集型任务执行时间:" + (endTime - startTime) + "ms"); } }
模拟执行总量相同的CPU密集型任务,通过修改核心线程数,看看执行时间是多少,得出的结果如下
核心线程数corePoolSize = 1时
核心线程数corePoolSize = availableProcessors + 1也就是CPU内核数加1时
核心线程数corePoolSize = availableProcessors * 2也就是CPU内核数*2时
结果差不多如预期一样,当核心线程数=CPU内核数加1时执行时间要比另外2种更短,效率更高。
二、IO密集型
-
介绍
IO密集型,是指需要大量的IO操作,例如文件读写、网络通信、数据库访问等。这种类型的任务主要涉及大量的IO操作,而CPU的计算能力相对较少。在执行IO密集型任务时,CPU内核的负载相对较轻,因为大部分时间都用于等待IO操作完成。IO密集型任务的性能瓶颈通常在于IO操作的速度和延迟,而不是CPU的计算能力。
-
与CPU内核的关系
IO密集型任务与CPU内核之间的关系在于任务的执行方式和对系统资源的需求。IO密集型任务通常需要大量的IO操作,例如文件读写、网络通信、数据库访问等,而对CPU计算资源的需求相对较少。
在自定义线程池中可以设置 核心线程数=CPU内核数*2,在处理IO密集型任务时,通常会出现大量的IO等待时间,而不是CPU计算时间。在这种情况下,可以充分利用系统中的多个CPU内核来处理其他线程的计算任务,从而提高系统的整体性能。
将核心线程数设置为CPU核数的两倍的原因是为了确保在执行IO密集型任务时,系统能够充分利用CPU内核来处理其他线程的计算任务,同时保持足够的线程数量来处理IO操作。这样可以更好地利用系统的资源,提高IO密集型任务的执行效率。
-
代码测试
public class CPUAndIOTest { public static void main(String[] args) { int availableProcessors = Runtime.getRuntime().availableProcessors(); System.out.println("CPU内核数:" + availableProcessors); // 测试IO密集型任务 testIOIntensiveTask(availableProcessors); } private static void testIOIntensiveTask(int availableProcessors) { long startTime = System.currentTimeMillis(); int corePoolSize = availableProcessors * 2; ExecutorService executor = Executors.newFixedThreadPool(corePoolSize); for (int i = 0; i < corePoolSize; i++) { executor.execute(() -> { // 模拟IO密集型任务 try { Thread.sleep(5000/corePoolSize); } catch (InterruptedException e) { e.printStackTrace(); } }); } executor.shutdown(); while (!executor.isTerminated()) { } long endTime = System.currentTimeMillis(); System.out.println("IO密集型任务执行时间:" + (endTime - startTime) + "ms"); } }
结果如下
核心线程数corePoolSize = 1时
核心线程数corePoolSize = availableProcessors + 1也就是CPU内核数加1时
核心线程数corePoolSize = availableProcessors * 2也就是CPU内核数*2时
从以上的结果可以看出,随着线程数的增加,IO密集型任务执行时间会逐渐变短,但线程过多也会导致线程的大量切换,造成资源的浪费,所以一般IO密集型任务设置的核心线程数为CPU内核数*2。
三、总结
-
CPU密集型:
核心线程数=CPU内核数+1
-
IO密集型:
核心线程数=CPU内核数*2