JDK源码Synchronized关键字原理,和锁的膨胀过程
Posted 一懒众衫小QAQ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK源码Synchronized关键字原理,和锁的膨胀过程相关的知识,希望对你有一定的参考价值。
Synchronized关键字原理,和锁的膨胀过程
一:Synchronized关键字介绍
synchronized是Java中的关键字,用于线程的同步。可以用在三个地方。
1:同步实例方法,锁是当前实例对象;
2:同步类方法,锁是当前类对象;
3:同步代码块,锁是括号里面的对象;
二:Synchronized的原理解析
1:synchronized是JVM内置锁,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁)实现。
我们对Synchronized的代码进行编译成字节码文件后,可以看到
public class SynchronizedDemo {
private final Object object = new Object();
@Test
public void test01() {
synchronized (object) {
System.out.println("true = " + true);
}
}
}
比如这段代码,我们锁的object对象,但是jvm怎么实现锁定对象的呢?
首先我们认识一下对象在jvm里面内存结构。
JVM锁的膨胀过程
内存中java对象是通过mark word在记录锁的状态。分表记录了轻量级锁,重量级锁,偏向锁,无锁的四种状态。
通过锁的标记位标记锁的状态。
比如现在线程T1进入获取锁的代码,首先判断这个锁对象(如:标记的object对象)首先检查锁的标志位,如果是01,然后再判断是否未偏向锁,检查标记是否未偏向锁的bit位是否位1,如果是1的话当前对象处于就是偏向锁,如果是0的话就是无锁状态,无锁的话,通过CAS修改锁的状态位偏向锁。
这个时候线程T2来获取锁,首先检查当前锁的状态,此时处于偏向锁状态。然后判断当前占用锁的线程ID是否位为自己。明显当前偏向锁的占有者是t1线程
这时候,t2线程会尝试 通过CAS修改,这个CAS循环获取锁的此时jvm可以有限制的,再指定次数获取失败后。
就说检查当前占用锁的t1线程是否走完同步代码块,如果走完了,设置当前占用锁的线程id为null。这个时候线程t2就可以获取到锁了。
如果t1没有走完同步代码块,还在进行同步逻辑。这个时候偏向锁就会升级为偏向锁,将锁的标记为设置为00(前面图中所示)。
这个时候锁的标记位如下图。
这个时候我们的t1线程,在线程栈占用开辟一块空间,执行我们的锁对象,同时锁对象中的Mark word中也会有空间指针指向,t1线程栈中的空间。如下图所示。
这个时候t2同样没有获取到锁,这时候t2同样通过CAS(一定次数)修改mark word中轻量级锁的指针。假如继续获取失败。
锁就会继续膨胀升级位重量级锁。这个时候锁的Mark word 如下图
这个时候,锁就进入了java6以前版本的模式了。t2就会被阻塞挂起来,等t1执行结束后重新被唤醒。
synchronized是JVM内置锁,通过内部对象Monitor(监视器锁)实现,基于进入与退出Monitor对象实现方法与代码块同步,监视器锁的实现依赖底层操作系统的Mutex lock(互斥锁》实现
总结
synchronized
在java6 以后做了升级,性能不输我们java.util.concurrent
包中的lock锁。同时我们看Synchronized
原理知道,我们在用Synchronized
关键字同步代码块的时候,锁定的范围尽量小,执行的时间尽量控制短一些。不然锁容易膨胀,性能就会下降。
以上是关于JDK源码Synchronized关键字原理,和锁的膨胀过程的主要内容,如果未能解决你的问题,请参考以下文章
JDK核心源码深入剖析(synchronized和ConcurrentHashMap)