Synchronized关键字算是Java的元老级锁了,一开始它撑起了Java的同步任务,其用法简单粗暴容易上手。但是有些与它相关的知识点还是需要我们开发者去深入掌握的。比如,我们都知道通过Synchronized锁来实现互斥功能,可以用在方法或者代码块上,那么不同用法都是怎么实现的,以及都经历了了哪些优化等等问题都需要我们扎实的理解。
1.基本用法
通常我们可以把Synchronized用在一个方法或者代码块里,方法又有普通方法或者静态方法。
public class TestSyn{
private int i=0;
public synchronized void incr(){
i++;
}
}
public class TestSyn{
private static int i=0;
public static synchronized void incr(){
i++;
}
}
public class TestSyn{
private int i=0;
Object o = new Object();
public void incr(){
synchronized(o){
i++;
}
}
}
2.实现原理
在JVM规范中介绍了synchronized的实现原理,JVM基于进入和退出Monitor对
象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,通过一个方法标志(flag) ACC_SYNCHRONIZED来实现的。
2.1 同步代码块的实现
monitorenter 和 monitorexit
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-6.html#jvms-6.5.monitorenter (参考来源)
下面看下JVM规范里对moniterenter 和 monitorexit的介绍
每个对象都有一个监视器(Moniter)与它相关联,执行moniterenter指令的线程将获得与objectref关联的监视器的所有权,如果另一个线程已经拥有与objectref关联的监视器,则当前线程将等待直到对象被解锁为止。
重点来了,上面这段介绍了两点:
- 通过monitorenter和monitorexit指令来实现Java语言的同步代码块(后面有代码示例)
- monitorenter和monitorexit指令没有被用在同步方法上!!!
2.2 同步方法的实现
先看下JVM规范里怎么说的
https://docs.oracle.com/javase/specs/jvms/se6/html/Compiling.doc.html#6530 (参考来源)
上面这段话主要讲了几点:
- 同步方法的实现不是基于monitorenter和monitorexit指令来实现的
- 在运行时常量池里通过ACC_SYNCHRONIZED来区分是否是同步方法,方法执行时会检查该标志
- 当一个方法有这个标志的时候,进入的线程首先需要获得监视器才能执行该方法
- 方法结束或者抛异常时会释放监视器
public class TestSyn {
private int i=0;
// 同步方法
public synchronized void incer(){
i++;
}
// 同步代码块
public void decr(){
synchronized (this) {
i--;
}
}
}
可以通过反编译字节码来查看底层是怎么实现的
// 得到字节码
javac TestSyn.java
// 反编译字节码
javap -v TestSyn.class
同步代码块的反编译结果如下:
同步方法的反编译结果如下:
3.锁升级
3.1 Java对象头介绍
对象的内存布局
在我们常见的HotSpot虚拟机中对象由三部分组成,分别是对象头,实例数据,以及对齐填充位。其中对象头是跟锁信息相关的部分,在对象头里会存储该对象运行时数据,包括哈希吗,GC分代年龄,锁状态(无锁,偏向锁,轻量级锁,重量级锁),是否偏向锁,偏向线程ID等信息。
存储上述这些的区域叫做Mark Word(标记词),除了这部分对象头还有一部分区域用来存储类型指针,可以通过该类型指针来定位对象的元数据信息。下面重点看下,对象头的内存布局,因为这部分是跟我们这次相关的。
对象在内存中的表示如下图:
对象头的结构表示如下图:
mark word的表示如下图:
3.2 什么是锁升级
下面举个抢茅坑的例子来解释一下锁升级过程。
假设我们每个厕所都有一把钥匙,要想使用厕所首先必须得获得锁。某天上午员工甲急急忙忙的打完卡上厕所了,并在厕所门上贴了 “工号007使用中”的标签,说明目前被工号007(相当于线程id)的员工占用呢,他再次向进入的时候只要上面的标签还显示工号007,他自己可以随便进入,不需要再次上锁了,有点偏向工号007员工的意思,所以这叫偏向锁。
员工甲正在使用厕所的时候,又来了两个人想用厕所,但发现厕所被人使用着呢,无法获得锁。所以只能在外面等着甲出来,他们等的过程叫做“自旋”,这个叫做轻量级锁。那么又有一个问题,当甲出来之后正等着的那两个人谁活得锁呢?有两种方式,按到达的顺序来排队或者不排队,这两种都可以实现,前者叫做公平锁,后者叫做非公平锁。
但那两个人自旋一段时间之后发现甲还没出来(JDK1.6规定为10次),一直这么等也不是个法子啊,所以打算向上升级,找厕所管理员(操作系统)反馈,升级成了重量级锁了
锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁。
锁升级过程中mark word的变化如下:
偏向锁
偏向锁也是JDK 1.6中引入的一项锁优化, 引入它是为了优化在没有锁竞争场景下的锁消除。比如一段同步代码一直是由单个线程调用,在这种场景下就没必要使用同步锁了,这里指的同步锁不是指synchronized,而是说没不要到操作系统层面的互斥量了。
偏向锁的偏向是指该同步代码会一直偏向第一个调用它的线程,直到有别的线程过来竞争这把锁,在第一次调用同步代码并获得锁时会在对象头和栈帧锁记录行(Lock Record)里存储偏向线程Id,该线程在此进入的时候就不需要重新申请锁了。只需检测对象头的Mark Word里是否存储着指向该线程的ID即可。
直到又有线程来竞争这把锁的时候偏向锁会撤销偏向。
轻量级锁
轻量级锁是JDK 1.6之中加入的新型锁机制, 它名字中的“轻量级”是相对于使用操作系统
互斥量来实现的传统锁而言的, 因此传统的锁机制就称为“重量级”锁。 它并不是用来代替重量级锁的, 它的本意是在统的重量级锁使用操作系统互斥量产生的性能消耗。
线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的Mark Word复制到锁记录中,官方称为Displaced Mark Word。然后线程尝试使用CAS将对象头中的Mark Word替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋来获取锁.一直原地自旋,如果自旋数达到10次了则升级为重量级锁。
重量级锁
竞争的线程自旋一段时间未能获取锁之后会升级为重量级锁,这个时候锁的获取与释放都会由操作系统来分配了,如果持有锁的线程释放锁之后操作系统会唤醒所有阻塞的哪些线程,并进入新一轮的争抢模式,需要注意的是这些阻塞的线程没有获得锁的优先级,也就是说synchronized锁是非公平的。除此之外synchronized对中断操作也是无感的,不会因为被中断而放弃阻塞等待,它要么得到锁要么一直阻塞。
http://market.szonline.net/amaz/20121.html
http://market.szonline.net/amaz/20120.html
http://market.szonline.net/amaz/20119.html
http://market.szonline.net/amaz/20118.html
http://market.szonline.net/amaz/20117.html
http://market.szonline.net/amaz/20116.html
http://market.szonline.net/amaz/20115.html
http://market.szonline.net/amaz/20113.html
http://market.szonline.net/amaz/20111.html
http://market.szonline.net/amaz/20108.html
http://market.szonline.net/amaz/20106.html
http://market.szonline.net/amaz/20103.html
http://market.szonline.net/amaz/20100.html
http://market.szonline.net/amaz/20098.html
http://market.szonline.net/amaz/20095.html
http://market.szonline.net/amaz/20093.html
http://market.szonline.net/amaz/20090.html
http://market.szonline.net/amaz/20088.html
http://market.szonline.net/amaz/20087.html
http://market.szonline.net/amaz/20086.html
http://market.szonline.net/amaz/20085.html
http://market.szonline.net/amaz/20084.html
http://market.szonline.net/amaz/20083.html
http://market.szonline.net/amaz/20082.html
http://market.szonline.net/amaz/20081.html
http://market.szonline.net/amaz/20080.html
http://market.szonline.net/amaz/20079.html
http://market.szonline.net/amaz/20078.html
http://market.szonline.net/amaz/20077.html
http://market.szonline.net/amaz/20076.html
http://market.szonline.net/amaz/20075.html
http://market.szonline.net/amaz/20074.html
http://market.szonline.net/amaz/20073.html
http://market.szonline.net/amaz/20072.html
http://market.szonline.net/amaz/20071.html
http://market.szonline.net/amaz/20070.html
http://market.szonline.net/amaz/20065.html
http://market.szonline.net/amaz/20059.html
http://market.szonline.net/amaz/20053.html
http://market.szonline.net/amaz/20047.html
http://market.szonline.net/amaz/20041.html
http://market.szonline.net/amaz/20037.html
http://market.szonline.net/amaz/20036.html
http://market.szonline.net/amaz/20035.html
http://market.szonline.net/amaz/20168.html
http://market.szonline.net/amaz/20167.html
http://market.szonline.net/amaz/20166.html
http://market.szonline.net/amaz/20165.html
http://market.szonline.net/amaz/20162.html
http://market.szonline.net/amaz/20161.html
http://market.szonline.net/amaz/20160.html
http://market.szonline.net/amaz/20159.html
http://market.szonline.net/amaz/20158.html
http://market.szonline.net/amaz/20157.html
http://market.szonline.net/amaz/20156.html
http://market.szonline.net/amaz/20155.html
http://market.szonline.net/amaz/20154.html
http://market.szonline.net/amaz/20153.html
http://market.szonline.net/amaz/20152.html
http://market.szonline.net/amaz/20151.html
http://market.szonline.net/amaz/20148.html
http://market.szonline.net/amaz/20144.html
http://market.szonline.net/amaz/20142.html
http://market.szonline.net/amaz/20141.html
http://market.szonline.net/amaz/20140.html
http://market.szonline.net/amaz/20139.html
http://market.szonline.net/amaz/20135.html
http://market.szonline.net/amaz/20131.html
http://market.szonline.net/amaz/20130.html
http://market.szonline.net/amaz/20129.html
http://market.szonline.net/amaz/20128.html
http://market.szonline.net/amaz/20127.html