【并发编程】synchronized

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(){

      synchronized (this) { // 此处自动加锁

     if (this.x < 12) {
        this.x = 12;
     }
  } // 此处自动解锁

}

 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”,表示此对象处于轻量级锁定状态。

 3.1.2 Klass Word

发布了111 篇原创文章 · 获赞 98 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/sarafina527/article/details/104710677