无聊望见了犹豫 达到理想不太易
胡思乱想,枯木逢春
本来计划一个月更新一到两篇技术文章,由于最近生病只能在家吃饭和多次复查,原本一些的空闲时间被辗转于医院和做饭占用,生病后才能更深刻体会健康的重要性。
另外由于搬家周末也是几乎都没有什么时间,搬东西时发现东西越搬越多,很多估计以后永远不会再用到的东西,看到这些想起小学时学过的一篇文章《哨子》,买了很多无用的东西同时又缺少断舍离的勇气,导致杂物缠身。以后买东西切记不要为一个“哨子”付出过高的代价,同时该舍弃时就要舍弃,太深的留恋反而变成了牵绊。
用户态与内核态
所有的JVM底层原理逃不开操作系统,从操作系统层面看程序分为内核态和用户态
什么是用户态和内核态
- 用户态:只能受限的访问内存,且不允许访问外围设备,占用cpu的能力被剥夺,cpu资源可以被其他程序获取。
- 内核态:cpu可以访问内存的所有数据,包括外围设备,例如硬盘,网卡,cpu也可以将自己从一个程序切换到另一个程序。
为什么要有用户态和内核态?
由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, 划分出两个权限等级 – 用户态和内核态。
为什么要讲用户态和内核态?
因为要理解syncronized锁升级就必须有这方面的基础,JVM更多的是定义规范实现很多种,本文主要介绍oracle实现的Hotspot版本,像IBM的J9、taobaoVM都有类似的实现。
syncronized锁升级
很多文章已经介绍过JDK早期(1.6之前不包括1.6),syncronized是重量级锁。
什么是锁
从古代的门闩、铁锁到现在的密码锁、指纹锁,锁的便携性和安全性不断提高,对私有财产保护更加高效和健全。在计算机世界里,单机线程时代里没有锁的概念。自从出现了资源竞争,我们才意识到需要对部分场景执行的现场加锁表明自己短暂的拥有。计算机开始的锁都是悲观锁,发展到现在乐观锁、偏向锁、分段锁等。锁主要提供了两种特性:互斥性和不可见性。因为有锁的存在,某些操作对外界来说是黑箱进行的,只有锁的持有者才知道对变量做了什么修改。
什么是重量级锁?
早期synchronized因为申请锁资源必须通过内核kernel系统调用,所以称为重量级锁。
为什么是重量级锁
为什么经过内核就是重量级锁了???这就要从synchronized底层说起,早期底层实现为了简便直接用了互斥锁,synchronized应该称为监视器锁(Monitor)本质是依赖于底层的操作系统的Mutex Lock(互斥锁)来实现的。每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象,这个互斥锁的CPU与内存的北桥信号或总线。同时,执行互斥锁需要操作系统的调用,由于Java的线程是映射到操作系统的原生线程之上的,如果要阻塞或唤醒一条线程,都需要操作系统来帮忙完成,这就需要从用户态转换到内核态中,状态转换需要耗费很多的处理器时间。所以synchronized是Java语言中的一个重量级操作,重量的原因是需要操作系统大哥帮忙调度,这就会涉及系统调用和中断,下面汇编代码简单说明系统调用过程
section .text
global _start
_start:
mov edx, len
mov ecx, msg
mov ebx, 1 ;文件描述符1 std_out
mov eax, 4 ;write函数系统调用号 4
int 0x80
mov ebx, 0
mov eax, 1 ;exit函数系统调用号
int 0x80
当程序执行系统调用时首先使用类似int 80H的软中断命令保存现场,去系统调用、内核执行、然后恢复现场,每个线程都会有两个栈,一个内核态栈和一个用户态栈。当中断执行时就会有用户态转到内核态,系统调用会执行栈的切换,而且内核态对用户态是不信任的,需要做一系列额外的检查,系统调用的返回过程需要很多检查比如是否需要调度、同时还要保存上下文,这都是说明为什么是重量级锁的原因。
为什么说是同步监视器
先看如下代码,一个简单的synchronized代码块
public class SynLock {
public void testSynBlock() {
synchronized (this) {
System.out.println("steven");
}
}
}
javac编译后可以看下字节码,如果用文本工具直接打卡都是16进制代码,除了最开始java版本基本看不懂,所以用javap反编译来看,javap可以把字节码编译成一些可读的代码形式,反编译class后如下: