锁的升级流程
首先我们都知道synchronized在jdk1.5以前的都是直接向操作系统直接申请重量级的锁,这样做很安全,但是就是会非常的慢。所以在之后的版本中我们的synchronized进行了一系列的优化,最开始出现了轻量级锁,相对与重量级锁来说是轻量的,就是不惊动我们的操作系统,通过CAS操作修改我们的对象内存地址中的markword信息来标志它为轻量级的锁(就相当与是操作系统大哥将锁直接放在门上谁拿到谁就可以开,不用来找他拿),什么是CAS操作:
- CAS compare and swap 它主要思想是 在操作之前先将我们的值保存一份 然后进行操作后的值再保存一份再操作后如果我们的保存的操作的值与当前值是一样的 意思就是没有其他的线程操作过该值, 那么我们就赋值操作如果不一样证明中途有线程修改过 那么就重复上述操作ABA问题 就是我们在操作过程中 如果该值经历过多次修改了 但是它的值确实不变的比如说 0 我们准备进行修改操作 但是在我们操作过程中其他线程 先修改为1 再修改为0 导致最后值不变的操作我们可以加一个时间或者其他的来标志 内核上是执行了一条汇编指令 同时这个指令多核cpu就会加锁修改值 单核就不会 防止在修改值之前被修改
但是自旋锁有一个缺点,就是如果一个线程已经持有锁了,那么其他线程就会执行类似的while循环操作去等待锁(自旋操作),这样造成的下场就是如果线程数量过多,或者等待时间过长,就会造成cpu算力的极大的浪费,所以它知识和在少量线程的处理。
在一段时间的轻量级锁的使用过后,java的团队就发现有很多时候都是同一个线程去竞争资源,相同的线程每次都去执行CAS操作太浪费我们的时间了,所以加入了偏向锁,如果是同一个线程来获取资源的话就直接把锁给他,怎么判断是否是同一个资源的,我们会将线程的id放入使用对象的markword位中,每次判断即可,如果有其他线程申请的话就会升级为轻量级的锁。
在java配置中有许多的值,下述的值就表示默认是用偏向锁,值默认为true,表示我们jvm中是默认使用偏向锁的。
但是还有一个参数,它表示的是等待几秒过后使用,这个主要是为了等待我们jvm的启动,一般jvm启动都是比较快的,所以可能导致有些对象创建好了还没有4s就没有加锁,就会如图1所示自动加上轻量级等的锁,所以我们可以修改这个值为0来让对象一开始就加上偏向锁。
整个流程就是我们的图1所示的。
对象在内存中的布局:
有无锁在我们的java对象内存结构中的markword标志位的区别
我们来看一看,不同锁到底修改了markword的哪个位置:
maven依赖 这个是打印对象存储结构的二进制信息
org.openjdk.jol jol-core 0.9
我们来看一看结果:
和我们上面那张图所显示的标志位相同,接下来我们让线程休眠4s,偏向锁默认启动4s后才加上,让他加上偏向锁再看:最后的结果也都是101符合上述图片。
我们需要注意的一点就是,我们偏向锁不像其他的锁,你进行竞争是才会加上,他是过完延迟时间后无论有无竞争操作,都默认加上。



