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。
锁升级
- 偏向锁:当线程获取到对象锁时(无竞争会将自己的线程id设置到对象头中),当再次获取时,查看对象头中记录的偏向锁的线程id是否一致,
如果一致则不需要cas设置。
如果不一致,因为锁是不能降级的,
所有这时会查看当前的线程id在栈帧中是否存活如果存活, 线程的栈帧已经不存在直接使用cas设置。
如果栈帧中存在对应的线程id,这是当前线程会撤销偏向锁,进入轻量级锁
- 轻量级->重量级锁:
当线程1已经成功设置偏向锁时,在DisplaceMarkword阶段(设置自己的线程id到对象头)
如果线程1成功设置,此时线程2cas设置时发现已经被设置,会撤销偏向锁(这个是有次数的当插销40次后会放弃偏向锁,升级进入到轻量级锁),进入自旋锁,(这里主要考虑用户态、内核态切换的时间消耗)故让线程2持有cpu进行自旋锁(-XX:PreBlockSpin一般是10可以设置具体查文档即可)。
线程2一直自旋(因此少量线程比较适合使用,如果n多个同时自旋,此时cpu在空转 空等,会出现cpu飙升,响应下降),
这时如果线程3进行cas设置,这是发现已经有线程2在竞争,则会升级锁到重量级锁(设置的也是对象头标记位)。使线程2、线程3进行阻塞状态。剩下的有操作系统调度管理了
- 话外篇
锁粗化:当锁的粒度变小会出现频繁的加锁、解锁,例如在一个方法内,没走一步都需要加锁、解锁,可以将各个步骤放在一次只加一次锁。减少锁动作
锁消除: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}
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"
评论