锁的源语-内核态用户态
关于java线程模型
https://www.bilibili.com/video/BV1ix41137Eu?from=search&seid=10570905734118987025 在Java中,基本我们说的线程(Thread)实际上应该叫作“用户线程”,而对应到操作系统,还有另外一种线程叫作“内核线程”。 用户线程和内核线程之间必然存在某种关系,多对一模型、一对一模型和多对多模型多对一线程模型
多个用户线程对应到同一个内核线程上,线程的创建、调度、同步的所有细节全部由进程的用户空间线程库来处理。
优点:用户线程的很多操作对内核来说都是透明的,不需要用户态和内核态的频繁切换,使线程的创建、调度、同步等非常快; 缺点:由于多个用户线程对应到同一个内核线程,如果其中一个用户线程阻塞,那么该其他用户线程也无法执行; 内核并不知道用户态有哪些线程,无法像内核线程一样实现较完整的调度、优先级等;一对一模型
即一个用户线程对应一个内核线程,内核负责每个线程的调度 优点:(比如JVM几乎把所有对线程的操作都交给了内核)实现线程模型的容器(jvm)简单,所以我们经常听到在java中使用线程一定要慎重就是这个原因; 缺点:对用户线程的大部分操作都会映射到内核线程上,引起用户态和内核态的频繁切换; 内核为每个线程都映射调度实体,如果系统出现大量线程,会对系统性能有影响;多对多模型
本篇文章暂不作介绍
内核态、用户态
如果有以上的认知,那么一个java的线程在运行的时候是内核态还是用户态呢? 其实这是个伪命题,因为一个软件级别的线程,用户态和内核态是不确定的。那么什么是内核态什么用户态呢? 这就要说到mmu和mmap了linux系统的虚拟地址映射
一、物理地址空间是什么
理解虚拟地址空间还得从物理地址空间开始说起。我们知道内存就像一个数组,每个存储单元被分配了一个地址,这个地址就是物理地址,所有物理地址构成的集合就是物理地址空间。物理地址也就是真实的地址,对应真实的那个内存条。二、虚拟地址空间是什么
引入虚拟地址之后,对于每一个进程,操作系统提供一种假象,让每个进程感觉自己拥有一个巨大的连续的内存可以使用,这个虚拟的空间甚至还可以比内存的容量还大。这个“假象”就是虚拟地址空间。虚拟地址是面向每个进程的,只是一个“假象”罢了。 CPU使用虚拟地址向内存寻址,通过专用的内存管理单元(MMU)硬件把虚拟地址转换为真实的物理地址。 intel x86 CPU有四种不同的执行级别0-3,linux只使用了其中的0级和3级分别来表示内核态和用户态,所谓的内核态和用户态其实仅仅是CPU的一个权限而已。用户态切换到内核态的3种方式
a. 系统调用 b. 异常(这个异常不是java当中的异常) c. 外围设备的中断 其实站在java程序员的角度只需要关注系统调用,因为系统调用可以认为是用户进程主动发起的,比如:调用线程的park()方法会对应到一个os的一个函数,从而使当前线程进入了内核态;再比如遇到synchronized关键字如果是重量锁则会调用pthread_mutex_lock()这样我们的线程也会切换到内核态;当执行完系统调用切换到用户态;什么是切换?有哪些切换
而在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好CPU 寄存器和程序计数器。什么是 CPU 上下文
和spring上下文差不多,CPU 寄存器和程序计数器就是 CPU 上下文,因为它们都是 CPU 在运行任何任务前,必须的依赖环境。- CPU 寄存器是 CPU 内置的容量小、但速度极快的内存。
- 程序计数器则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。