Synchronizd Lock 锁升级 AQS

Synchronized

在1.5以前是重量级锁,当遇到synchronzed时,其他线程需要等待并进入blocked状态。

在1.6以后,经过大量的测试发现加锁的地方经常被一个线程访问。所以对synchronized进行了锁的粒度优化,主要在对象头增加标记位因此有了偏向锁、轻量级锁、重量级锁。

三种常用用法Synchronized(A.class), synchronized(变量) synchronized method, 通过代码与bytecode对比发现,

  • synchronized method: 字节码中存在ACC_SYNCHRONIZED这个标记,当执行到这个行是,当前线程需要获取对象monitor锁,然后在进入方法处理业务,当业务正常退出方法或者异常都会自动释放monitor锁,这个是隐式运行的(更深入一点的这个ACC_SYNCHRONIZED在运行常量池中。method_info中的access_flags,但是在jdk8 bytecode没有发现这个标记。)

  • synchronized(): 在字节码中增加了monitorenter、monitorexit。

  • 锁升级

  1. 偏向锁:当线程获取到对象锁时(无竞争会将自己的线程id设置到对象头中),当再次获取时,查看对象头中记录的偏向锁的线程id是否一致,

如果一致则不需要cas设置。

如果不一致,因为锁是不能降级的,

所有这时会查看当前的线程id在栈帧中是否存活如果存活, 线程的栈帧已经不存在直接使用cas设置。

如果栈帧中存在对应的线程id,这是当前线程会撤销偏向锁,进入轻量级锁
  1. 轻量级->重量级锁:

当线程1已经成功设置偏向锁时,在DisplaceMarkword阶段(设置自己的线程id到对象头)

如果线程1成功设置,此时线程2cas设置时发现已经被设置,会撤销偏向锁(这个是有次数的当插销40次后会放弃偏向锁,升级进入到轻量级锁),进入自旋锁,(这里主要考虑用户态、内核态切换的时间消耗)故让线程2持有cpu进行自旋锁(-XX:PreBlockSpin一般是10可以设置具体查文档即可)。

线程2一直自旋(因此少量线程比较适合使用,如果n多个同时自旋,此时cpu在空转 空等,会出现cpu飙升,响应下降),

这时如果线程3进行cas设置,这是发现已经有线程2在竞争,则会升级锁到重量级锁(设置的也是对象头标记位)。使线程2、线程3进行阻塞状态。剩下的有操作系统调度管理了

  1. 话外篇

锁粗化:当锁的粒度变小会出现频繁的加锁、解锁,例如在一个方法内,没走一步都需要加锁、解锁,可以将各个步骤放在一次只加一次锁。减少锁动作

锁消除:JIT编译、逃逸分析等,将不可能存在共享资源竞争的锁去除了。

当线程持有锁后,如果这个线程内部执行动作比较耗时。这个程序时是没法被终止的,只能等自己退出、或者异常退出。后续的阻塞的线程才能重新获取锁。针对这种情况出现了Lock的概念。

Lock

锁可控、锁重入、公平、非公平

Lock在jdk底层开发了api,unsafe的方法,其中主要依赖顶层的AQS。线程对共享资源state变量进行cas设置,如果设置成功,设置线程持有锁,其他线程在设置失败后会有个短暂的自旋。然后会被LockSupport.park(this)【通过堆栈信息线程状态是WAITING(park, wait,sleep)】将当前线程挂起并加入到CLH队列中(是一个双向链表。尾插入,当前驱节点异常或者退出会自动替换链表的关系)。

  • ReentrantLock ArrayBlockingQueue内部定义了notFull,noEmpty,ReetrantLock,用来控制队列的put,take阻塞行为
    • Condition: 代替Object的wait、notify、notifyAll 为什么没有Object中的wait、notify。这个需要在synchronize()块中。不灵活
    • ArrayBlockingQueue: 数组阻塞队列
    • LinkedBlockingQueue: 阻塞连接队列
    • LinkedTransferQueue: 无界链表队列
  • AQS
  • CountDownLatch
  • Semaphore

参考

源码

 1package cn.linuxcrypt.api.demo;
 2
 3public class SynchronizedTest {
 4    public synchronized void test1() {
 5        System.out.println("00");
 6    }
 7
 8    public void test2() {
 9        synchronized (this) {
10            System.out.println("bb");
11        }
12    }
13
14    public void test3() {
15        synchronized (SynchronizedTest.class) {
16            System.out.println("cc");
17        }
18    }
19
20
21    public static void main(String[] args) {
22        SynchronizedTest t = new SynchronizedTest();
23
24
25    }
26}
27
  1Classfile .../target/classes/cn/linuxcrypt/api/demo/SynchronizedTest.class
  2  Last modified 2020-8-21; size 1062 bytes
  3  MD5 checksum 8c7b7c349fb0f4d9e7a7526673e8f33b
  4  Compiled from "SynchronizedTest.java"
  5public class cn.linuxcrypt.api.demo.SynchronizedTest
  6  minor version: 0
  7  major version: 52
  8  flags: ACC_PUBLIC, ACC_SUPER
  9Constant pool:
 10   #1 = Methodref          #9.#32         // java/lang/Object."<init>":()V
 11   #2 = Fieldref           #33.#34        // java/lang/System.out:Ljava/io/PrintStream;
 12   #3 = String             #35            // 00
 13   #4 = Methodref          #36.#37        // java/io/PrintStream.println:(Ljava/lang/String;)V
 14   #5 = String             #38            // bb
 15   #6 = Class              #39            // cn/linuxcrypt/api/demo/SynchronizedTest
 16   #7 = String             #40            // cc
 17   #8 = Methodref          #6.#32         // cn/linuxcrypt/api/demo/SynchronizedTest."<init>":()V
 18   #9 = Class              #41            // java/lang/Object
 19  #10 = Utf8               <init>
 20  #11 = Utf8               ()V
 21  #12 = Utf8               Code
 22  #13 = Utf8               LineNumberTable
 23  #14 = Utf8               LocalVariableTable
 24  #15 = Utf8               this
 25  #16 = Utf8               Lcn/linuxcrypt/api/demo/SynchronizedTest;
 26  #17 = Utf8               test1
 27  #18 = Utf8               test2
 28  #19 = Utf8               StackMapTable
 29  #20 = Class              #39            // cn/linuxcrypt/api/demo/SynchronizedTest
 30  #21 = Class              #41            // java/lang/Object
 31  #22 = Class              #42            // java/lang/Throwable
 32  #23 = Utf8               test3
 33  #24 = Utf8               main
 34  #25 = Utf8               ([Ljava/lang/String;)V
 35  #26 = Utf8               args
 36  #27 = Utf8               [Ljava/lang/String;
 37  #28 = Utf8               t
 38  #29 = Utf8               MethodParameters
 39  #30 = Utf8               SourceFile
 40  #31 = Utf8               SynchronizedTest.java
 41  #32 = NameAndType        #10:#11        // "<init>":()V
 42  #33 = Class              #43            // java/lang/System
 43  #34 = NameAndType        #44:#45        // out:Ljava/io/PrintStream;
 44  #35 = Utf8               00
 45  #36 = Class              #46            // java/io/PrintStream
 46  #37 = NameAndType        #47:#48        // println:(Ljava/lang/String;)V
 47  #38 = Utf8               bb
 48  #39 = Utf8               cn/linuxcrypt/api/demo/SynchronizedTest
 49  #40 = Utf8               cc
 50  #41 = Utf8               java/lang/Object
 51  #42 = Utf8               java/lang/Throwable
 52  #43 = Utf8               java/lang/System
 53  #44 = Utf8               out
 54  #45 = Utf8               Ljava/io/PrintStream;
 55  #46 = Utf8               java/io/PrintStream
 56  #47 = Utf8               println
 57  #48 = Utf8               (Ljava/lang/String;)V
 58{
 59  public cn.linuxcrypt.api.demo.SynchronizedTest();
 60    descriptor: ()V
 61    flags: ACC_PUBLIC
 62    Code:
 63      stack=1, locals=1, args_size=1
 64         0: aload_0
 65         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
 66         4: return
 67      LineNumberTable:
 68        line 3: 0
 69      LocalVariableTable:
 70        Start  Length  Slot  Name   Signature
 71            0       5     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;
 72
 73  public synchronized void test1();
 74    descriptor: ()V
 75    flags: ACC_PUBLIC, ACC_SYNCHRONIZED
 76    Code:
 77      stack=2, locals=1, args_size=1
 78         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
 79         3: ldc           #3                  // String 00
 80         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
 81         8: return
 82      LineNumberTable:
 83        line 5: 0
 84        line 6: 8
 85      LocalVariableTable:
 86        Start  Length  Slot  Name   Signature
 87            0       9     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;
 88
 89  public void test2();
 90    descriptor: ()V
 91    flags: ACC_PUBLIC
 92    Code:
 93      stack=2, locals=3, args_size=1
 94         0: aload_0
 95         1: dup
 96         2: astore_1
 97         3: monitorenter
 98         4: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
 99         7: ldc           #5                  // String bb
100         9: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
101        12: aload_1
102        13: monitorexit
103        14: goto          22
104        17: astore_2
105        18: aload_1
106        19: monitorexit
107        20: aload_2
108        21: athrow
109        22: return
110      Exception table:
111         from    to  target type
112             4    14    17   any
113            17    20    17   any
114      LineNumberTable:
115        line 9: 0
116        line 10: 4
117        line 11: 12
118        line 12: 22
119      LocalVariableTable:
120        Start  Length  Slot  Name   Signature
121            0      23     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;
122      StackMapTable: number_of_entries = 2
123        frame_type = 255 /* full_frame */
124          offset_delta = 17
125          locals = [ class cn/linuxcrypt/api/demo/SynchronizedTest, class java/lang/Object ]
126          stack = [ class java/lang/Throwable ]
127        frame_type = 250 /* chop */
128          offset_delta = 4
129
130  public void test3();
131    descriptor: ()V
132    flags: ACC_PUBLIC
133    Code:
134      stack=2, locals=3, args_size=1
135         0: ldc           #6                  // class cn/linuxcrypt/api/demo/SynchronizedTest
136         2: dup
137         3: astore_1
138         4: monitorenter
139         5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
140         8: ldc           #7                  // String cc
141        10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
142        13: aload_1
143        14: monitorexit
144        15: goto          23
145        18: astore_2
146        19: aload_1
147        20: monitorexit
148        21: aload_2
149        22: athrow
150        23: return
151      Exception table:
152         from    to  target type
153             5    15    18   any
154            18    21    18   any
155      LineNumberTable:
156        line 15: 0
157        line 16: 5
158        line 17: 13
159        line 18: 23
160      LocalVariableTable:
161        Start  Length  Slot  Name   Signature
162            0      24     0  this   Lcn/linuxcrypt/api/demo/SynchronizedTest;
163      StackMapTable: number_of_entries = 2
164        frame_type = 255 /* full_frame */
165          offset_delta = 18
166          locals = [ class cn/linuxcrypt/api/demo/SynchronizedTest, class java/lang/Object ]
167          stack = [ class java/lang/Throwable ]
168        frame_type = 250 /* chop */
169          offset_delta = 4
170
171  public static void main(java.lang.String[]);
172    descriptor: ([Ljava/lang/String;)V
173    flags: ACC_PUBLIC, ACC_STATIC
174    Code:
175      stack=2, locals=2, args_size=1
176         0: new           #6                  // class cn/linuxcrypt/api/demo/SynchronizedTest
177         3: dup
178         4: invokespecial #8                  // Method "<init>":()V
179         7: astore_1
180         8: return
181      LineNumberTable:
182        line 22: 0
183        line 25: 8
184      LocalVariableTable:
185        Start  Length  Slot  Name   Signature
186            0       9     0  args   [Ljava/lang/String;
187            8       1     1     t   Lcn/linuxcrypt/api/demo/SynchronizedTest;
188    MethodParameters:
189      Name                           Flags
190      args
191}
192SourceFile: "SynchronizedTest.java"
193