1.功能
synchronized是用与线程同步,是一个重量级互斥锁,可以保证方法或者代码块在运行时,同一时刻只有一个线程可以进入到该对象的临界区(同一个对象的所有synchronized保卫的代码块)。
synchronized是可重入的,即同一个线程,可以多次进入同个对象的多个synchronized块。
2.锁对象
synchronized修饰位置 | 锁对象 | 样例 |
---|---|---|
实例方法 | 当前实例对象 this | public synchronized void synMethod() |
静态方法 | 当前类的Class对象 | public static synchronized void doSth() |
代码块 | 括号里面的对象, 既可以是实例对象 也可以是类对象 |
synchronized (lock) {...} synchronized (lock.getClass()) {...} |
3.JVM实现
下面看下一个同步块编译后的虚拟机指令是什么样的,
方法 | .class类文件指令 |
---|---|
|
public void synBlock(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=3, args_size=1 0: aload_0 1: dup 2: astore_1 3: monitorenter 4: aload_0 5: getfield #2 // Field x:I 8: bipush 12 10: if_icmpge 19 13: aload_0 14: bipush 12 16: putfield #2 // Field x:I 19: aload_1 20: monitorexit 21: goto 29 24: astore_2 25: aload_1 26: monitorexit 27: aload_2 28: athrow 29: return Exception table: from to target type 4 21 24 any 24 27 24 any LineNumberTable: line 15: 0 line 17: 4 line 18: 13 line 20: 19 line 22: 29 LocalVariableTable: Start Length Slot Name Signature 0 30 0 this .... |
synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令,其中 monitorenter 指向同步代码块的开始位置,monitorexit 指向结束位置。
3.1 Java对象头
对象头:64 | |
Mark Word:32 | Klass Word:32 |
数组对象头:96 | ||
Mark Word:32 | Klass Word:32 | Array Length:32 |
3.1.1 Mark Word
Mark Word是动态定义的数据结构,在对象不同锁状态时,存储不同的数据,以便减少内存消耗,如下图
锁状态 | Mark Word | ||||
---|---|---|---|---|---|
未锁定 Normal | hashcode:25 哈希码 |
age:4 分代年龄 |
biased_lock:1 0 |
lock:2 01 |
|
偏向锁 Biased | thread:23 偏向线程 |
epoch:2 偏向时间戳 |
age:4 分代年龄 |
biased_lock:1 1 |
lock:2 01 |
轻量级锁 Light | ptr_to_lock_record:30 指向锁记录的指针 |
lock:2 00 |
|||
重量级锁 Heavy | ptr_to_heavyweight_monitor:30 指向重量级锁的指针 |
lock:2 10 |
|||
GC标记 | 30 空 |
lock:2 11 |
在代码即将进入同步块的时候,如果此同步对象没有被锁定(lock为“01”状态),且biased_lock=0
-XX:+UseBiased Locking 偏向锁可用 |
1.lock=01,biased_lock=1, 2.CAS操作threadID到mark word(前23bit) |
---|---|
偏向锁不可用,(jdk6以前) 用轻量级锁 |
1.在栈上锁记录lock record拷贝mark word 2.CAS操作lock record指针到mark word |
锁膨胀到重量级锁 |
偏向锁
偏向锁也是JDK 6中引入的一项锁优化措施,目的是消除在无竞争情况下的同步原语,连CAS操作都不去做,进一步提高运行性能。。偏向锁可以通过-XX:+UseBiased Locking参数选择开启,从JDK6后默认开启。
当锁对象第一次被线程获取的时候,虚拟机将会把对象头中的标志位设置为“01”、把偏向模式设置为“1”,表示进入偏向模式。
一旦另外一个线程去竞争这个锁,偏向模式就马上宣告结束。根据锁对象目前是否处于被锁定的状态决定是否撤销偏向(偏向模式设置为“0”),撤销后标志位恢复到未锁定(标志位为“01”)或轻量级锁定(标志位为“00”)的状态。
轻量级锁
虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,如图所示。
使用CAS操作尝试把对象的Mark Word (前30bit)更新为指向Lock Record的指针。如果这个更新动作成功了,即代表该线程拥有了这个对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后两个比特)将转变为“00”,表示此对象处于轻量级锁定状态。
虚拟机将使用CAS操作尝试把对象的Mark Word更新为指向Lock Record的指针。如果这个更新动作成功了,即代表该线程拥有了这个对象的锁,并且对象Mark Word的锁标志位(Mark Word的最后两个比特)将转变为“00”,表示此对象处于轻量级锁定状态。