问题出现的原因是Synchronize和ReentrantLock二者的都会阻塞线程,并且阻塞和唤醒的代价高
操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善
用户态和内核态: 内核从本质上看是一种软件——控制计算机的硬件资源,并提供上层应用程序运行的环境。用户态即上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用。
损耗:几十纳秒到数微妙的CPU时间
- 上下文切换种类
1.线程切换-同一进程中的两个线程之间的切换
2.进程切换-两个进程之间的切换
3.模式切换-在给定线程中,用户模式和内核模式的切换
在上下文切换过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行
在程序中上下文切换过程中的“页码”信息是保存在进程控制块
(PCB, process control block),切换帧
PCB通常是系统内存占用区中的一个连续存区,它存放着操作系统用于描述进程情况及控制进程运行所需的全部信息
-上下文切换执行步骤:
- 保存进程A的状态(寄存器和操作系统数据);
- 更新PCB中的信息,对进程A的“运行态”做出相应更改;
- 将进程A的PCB放入相关状态的队列;
- 将进程B的PCB信息改为“运行态”,并执行进程B;
- B执行完后,从队列中取出进程A的PCB,恢复进程A被切换时的上下文,继续执行A;
- CPU 上下文切换:
把前一个任务的CPU上下文(CPU寄存器和程序计数器-内核栈)保存,然后加载新任务的上下文到寄存器和程序计数器中,跳转到程序计数器所指向的位置,开始执行任务。 - 进程切换:进程资源(用户态的虚拟内存、栈,内核态的栈和硬件上下文-CPU寄存器)
1.切换页目录以使用新的地址空间(刷新新进程的虚拟内存和用户栈,当虚拟内存更新后,TLB也需要更新)
2.切换内核栈和硬件上下文 - 线程切换:
1.切换内核栈和硬件上下文
线程的切换虚拟内存空间依然是相同的(TLB),但是进程切换是不同的
多线程上下文切换的主要损耗
linux2.6 jvm1.8采用的是1对1线程模型,线程调度是由os内核完成(内核态),同一个进程种多线程的上下文切换不需要更新虚拟内存,而且只有一次cpu上下文切换
有一点不能混淆:
cpu时间片,不同进程想要执行必须获得cpu时间片,这个是由内核决定,完全公平调度
线程调度就是由内核来完成的,这个过程中,应该涉及到的是内核程序获取cpu时间片,执行内核态操作,主要损耗是在cpu内核和用户进程切换导致的损耗,这个是无法避免的
一次系统调用主要损耗
从用户态切换到内核态,需要通过系统调用的方式。该过程也是有CPU上下文切换的:切换时,先保存CPU寄存器中用户态的指令位置,再重新更新为内核指令的位置。当系统调用结束时,CPU寄存器恢复到原来保存的用户态。一次系统调用,发生了两次CPU上下文切换
。
进程切换主要损耗:
- 指的是CPU寄存器需要保存和加载(单纯切换寄存器影响倒不是特别大)
- TLB(TLB是一种高速缓存,内存管理硬件使用它来改善虚拟地址到物理地址的转换速度)实例需要重新加载-这个对性能影响非常大不说,整个进程的执行都会停止
- CPU 的pipeline需要刷掉(cpu 汇编代码优化-汇编代码转换成机器指令由硬件直接实现这一步速度是很快的)
- 调度器进行线程调度
- 间接消耗:性能损失来源于缓冲区的复制,多核cache的复制
补充:
1、对于一个正在执行的进程包括 程序计数器、寄存器、变量的当前值等 ,而这些数据都是 保存在CPU的寄存器中的,且这些寄存器只能是正在使用CPU的进程才能享用,在进程切换时,首先得保存上一个进程的这些数据,然后将本次获得CPU的进程的这些数据装入CPU的寄存器从上次断点处继续执行剩下的任务。
2、当程序中有系统调用语句,程序执行到系统调用时,首先使用类似int 80H的软中断指令,保存现场,去的系统调用号,在内核态执行,然后恢复现场,每个进程都会有两个栈,一个内核态栈和一个用户态栈。当执行int中断执行时就会由用户态,栈转向内核栈,系统调用时需要进行栈的切换。
3、系统调用就会引起用户态和内核态的切换
系统调用一般都需要保存用户程序得上下文(context), 在进入内核得时候需要保存用户态得寄存器,在内核态返回用户态得时候会恢复这些寄存器得内容。这是一个开销的地方。 如果需要在不同用户程序间切换的话,那么还要更新cr3寄存器,这样会更换每个程序的虚拟内存到物理内存映射表的地址(PCB),也是一个比较高负担的操作。
科普
寄存器是中央处理器内的组成部份。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。在中央处理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序计数器(PC)。在中央处理器的算术及逻辑部件中,包含的寄存器有累加器(ACC)。
内存包含的范围非常广,一般分为只读存储器(ROM)、随机存储器(RAM)和高速缓存存储器(cache)。
寄存器是CPU内部的元件,寄存器拥有非常高的读写速度,所以在寄存器之间的数据传送非常快。
Cache :即高速缓冲存储器,是位于CPU与主内存间的一种容量较小但速度很高的存储器。由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待一定时间周期,Cache中保存着CPU刚用过或循环使用的一部分数据,当CPU再次使用该部分数据时可从Cache中直接调用,这样就减少了CPU的等待时间,提高了系统的效率。Cache又分为一级Cache(L1 Cache)和二级Cache(L2 Cache),L1 Cache集成在CPU内部,L2 Cache早期一般是焊在主板上,现在也都集成在CPU内部,常见的容量有256KB或512KB L2 Cache。
查看
cs列就是指上下文切换的数目. 一般情况下, 空闲系统的上下文切换每秒大概在1500以下.
vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 11080100 640552 45502336 0 0 72 323 0 0 13 3 84 0 0
0 0 0 11079984 640552 45502336 0 0 0 0 3233 5797 1 0 98 0 0
0 0 0 11079968 640552 45502336 0 0 0 0 2975 6137 1 0 99 0 0
1 0 0 11079984 640552 45502336 0 0 0 68 3870 6188 4 0 96 0 0
1 0 0 11080000 640552 45502336 0 0 0 4 3671 6122 2 0 98 0 0
0 0 0 11079860 640552 45502336 0 0 0 0 2900 6360 1 0 99 0 0
0 0 0 11079860 640552 45502336 0 0 0 0 3616 6021 2 0 98 0 0
0 0 0 11078776 640552 45502336 0 0 0 8 4062 7365 2 1 97 0 0
0 0 0 11078744 640552 45502336 0 0 0 64 2970 6091 1 0 99 0 0
1 0 0 11078760 640552 45502336 0 0 0 16 3764 6137 3 0 97 0 0
0 0 0 11078760 640552 45502336 0 0 0 0 3175 6207 1 0 99 0 0
0 0 0 11078744 640552 45502336 0 0 0 0 3011 6025 1 0 98 0 0
0 0 0 11079264 640552 45502328 0 0 0 44 4071 6573 2 0 97 0 0
感谢大神:
https://juejin.im/post/5b10e53b6fb9a01e5b10e9be#heading-14
https://blog.csdn.net/JH_Zhai/article/details/79861169
https://cloud.tencent.com/developer/article/1082708
https://my.oschina.net/u/4106059/blog/3029071
https://blog.csdn.net/Code_Hu/article/details/84632591