线程切换
- CPU给线程分配时间片(也就是分配给线程的时间),执行完时间片后会切换到另一个线程。
- 切换之前会保存线程的状态,下次时间片再给这个线程时才能知道当前状态。
- 从保存线程A的状态再到切换到线程B时,重新加载线程B的状态的这个过程就叫上下文切换。
- 而上下切换时会消耗大量的CPU时间。
CPU运行状态分为用户态和内核态。 线程切换状态会使CPU运行状态从用户态转换到内核态。如果切换内存状态,需要先把本线程的代码和变量写入内存。
线程开销
- 上下文切换消耗
- 线程创建和消亡的开销
- 线程需要保存维持线程本地栈,会消耗内存
对于内存而言,堆内存、代码区一般属于一个进程,但是栈却是属于一个线程的,且每个线程拥有一个独立的栈。
线程池合理配置线程数
-
CPU密集型
CPU密集型的意思就是该任务需要大量运算,而没有阻塞,CPU一直全速运行。
CPU密集型任务只有在真正的多核CPU上才可能得到加速(通过多线程)。
CPU密集型任务配置尽可能少的线程数,开太多线程反而会因为线程切换时切换上下文而浪费资源。
CPU密集型线程数配置公式:CPU核数+1个线程的线程池 -
IO密集型
IO密集型,即该任务需要大量的IO,即大量的阻塞,比如磁盘IO(读取文件)和网络IO(网络请求)。
在单线程上运行IO密集型任务会导致浪费大量的CPU运算能力浪费在等待。
因为IO操作会阻塞线程,CPU利用率不高,可以开多点线程,阻塞时可以切换到其他就绪线程,提高CPU利用率。
所以IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要利用了被浪费掉的阻塞时间。
2.1 第一种配置方式:
由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程。
配置公式(市场一般用这种):CPU核数 * 2。
2.2 第二种配置方式:
IO密集型时,大部分线程都阻塞,故需要多配置线程数。
配置公式:CPU核数 / 1 – 阻塞系数(0.8~0.9之间)
比如:8核 / (1 – 0.9) = 80个线程数
代码获取核数
// 可以的处理器
Runtime.getRuntime().availableProcessors()