文章目录
前言
synchronized原理分析。
一、synchronized是如何锁住this对象的?
堆存放的实例对象信息会有一个对象头,指向方法区中的类(类中存有类方法和类的静态变量)对象,这个对象头就是synchronized需要锁住的东西,其中对象头部包含:Mark Word(堆中的一段内存区域)、class meta address(堆指向类对象的指针或者叫地址)、array length(数组对象的长度)。最终,synchronized是根据cas修改this对象的Mark Word。
mark word:
二、synchronized中CAS修改过程
1.未锁定变成轻量级锁的原理
首先,线程会把堆中的原mark word中的状态复制到线程中,通过CAS修改mark word ,将未锁定(关闭偏向锁)修改成轻量级锁,成功则抢锁成功,失败的线程则是自旋(长时间的自旋,会占用cpu性能,然后会锁升级)
代码如下(示例):
2.轻量级锁升级为重量级锁的原理
代码如下(示例):
轻量级锁升级为重量级锁,对象头的状态变更了,然后对象头中的信息还需要存线程池队列(entryList)和(onwer)线程获取锁就是获取onwer,还有一个等待队列wait set。重量级抢锁跟原来写的testlock差不多。
三:锁升级的过程
图示:
未锁定,开启偏向锁的状态–>偏向锁 锁定 这个过程是属于单线程的时候才会发生,而且他加锁以后不会在解锁了。偏向锁加锁成功后,又有其他线程来访问,这时候线程会升级变成轻量级锁或者升级为未锁定,关闭偏向锁。重量级解锁会直接回到未锁定,关闭偏向锁(只是关闭了当前对象的锁)。
四:锁粗化和锁消除
锁粗化:比如一段代码有多个地方用到synchronized,然后此段代码多次执行以后,将整个代码块放到一个synchronized中,类似这样的操作就是锁粗化。不在意是否多个线程访问(也是所合并)。
示例:test()粗化成test1()
public void test(Object arg) {
synchronized (this){
i++;
}
synchronized (this){
i--;
}
//生成随机数
synchronized (this){
//生成随机数
}
synchronized (this){
i--;
}
}
public void test1(Object arg) {
synchronized (this){
i++;
i--;
//生成随机数
//生成随机数
i++;
}
}
锁消除:例如:多个StringBuffer.append(),连续执行就会导致频繁的加锁解锁,就会触发JIT编译将append()的synchronized消除掉。注意:StringBuffer是个局部变量,没有在其他线程中使用,只能有一个线程访问。
总结
synchronized底层原理就是CAS操作mark word,将修改mark word为指定的状态(锁状态:未锁定,偏向锁,轻量级锁,重量级锁)。synchronized优化方案:锁粗化,锁消除,偏向锁,轻量级锁,重量级锁;synchronized属于互斥锁。