日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java并发synchronized 锁的膨胀过程(锁的升级过程)深入剖析(1)

發布時間:2023/11/30 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java并发synchronized 锁的膨胀过程(锁的升级过程)深入剖析(1) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們先來說一下我們為什么需要鎖?

因為在并發情況為了保證線程的安全性,是在一個多線程環境下正確性的概念,也就是保證多線程環境下共享的、可修改的狀態的正確性(這里的狀態指的是程序里的數據),在java程序中我們可以使用synchronized關鍵字來對程序進行加鎖。

當聲明synchronized代碼塊的時候,編譯成的字節碼將包含monitorenter指令?和?monitorexit指令。這兩種指令均會消耗操作數棧上的一個引用類型的元素(也就是 synchronized 關鍵字括號里的引用),作為所要加鎖解鎖的鎖對象。

(注意:jdk 1.6以前synchronized 關鍵字只表示重量級鎖,1.6之后區分為偏向鎖、輕量級鎖、重量級鎖。)

?

所謂鎖的升級、降級,就是 JVM 優化 synchronized 運行的機制,當 JVM 檢測到不同的競爭狀況時,會自動切換到適合的鎖實現,這種切換就是鎖的升級、降級:

  • 當沒有競爭出現時,默認會使用偏向鎖。JVM 會利用 CAS 操作(compare and swap),在對象頭上的 Mark Word 部分設置線程 ID,以表示這個對象偏向于當前線程,所以并不涉及真正的互斥鎖。這樣做的假設是基于在很多應用場景中,大部分對象生命周期中最多會被一個線程鎖定,使用偏向鎖可以降低無競爭開銷。

  • 如果有另外的線程試圖鎖定某個已經被偏向過的對象,JVM 就需要撤銷(revoke)偏向鎖,并切換到輕量級鎖實現。輕量級鎖依賴 CAS 操作 Mark Word 來試圖獲取鎖,如果重試成功,就使用輕量級鎖;否則,進一步升級為重量級鎖

?

那么我們來看段synchronized代碼分析:

java代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

public?class?TestDemo {

}

public?class?DemoExample1 {

????static?TestDemo testDemo;

????public?static?void?main(String[] args) throws Exception {

????????testDemo=?new?TestDemo();

????????synchronized (testDemo){

????????????System.out.println("lock ing");

????????????testDemo.hashCode();

????????????System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

????????}

????}

}

?

運行并分析TestDemo.class文件命令:

1

javap -c DemoExample1.class

?

分析結果:

Compiled from "DemoExample1.java"

public class com.boke.DemoExample1 {

??static com.boke.TestDemo testDemo;

?

??public com.boke.DemoExample1();

????Code:

???????0: aload_0

???????1: invokespecial #1??????????????????// Method java/lang/Object."<init>":()V

???????4: return

?

??public static void main(java.lang.String[]) throws java.lang.Exception;

????Code:

???????0: new???????????#2??????????????????// class com/boke/TestDemo

???????3: dup

???????4: invokespecial #3??????????????????// Method com/boke/TestDemo."<init>":()V

???????7: putstatic?????#4??????????????????// Field testDemo:Lcom/boke/TestDemo;

??????10: getstatic?????#4??????????????????// Field testDemo:Lcom/boke/TestDemo;

??????13: dup

??????14: astore_1

??????15:?monitorenter

??????16: getstatic?????#5??????????????????// Field java/lang/System.out:Ljava/io/PrintStream;

??????19: ldc???????????#6??????????????????// String lock ing

??????21: invokevirtual #7??????????????????// Method java/io/PrintStream.println:(Ljava/lang/String;)V

??????24: getstatic?????#4??????????????????// Field testDemo:Lcom/boke/TestDemo;

??????27: invokevirtual #8??????????????????// Method java/lang/Object.hashCode:()I

??????30: pop

??????31: getstatic?????#5??????????????????// Field java/lang/System.out:Ljava/io/PrintStream;

??????34: getstatic?????#4??????????????????// Field testDemo:Lcom/boke/TestDemo;

??????37: invokestatic??#9??????????????????// Method org/openjdk/jol/info/ClassLayout.parseInstance:(Ljava/lang/Object;)Lorg/openjdk/jol/info/ClassLayout;

??????40: invokevirtual #10?????????????????// Method org/openjdk/jol/info/ClassLayout.toPrintable:()Ljava/lang/String;

??????43: invokevirtual #7??????????????????// Method java/io/PrintStream.println:(Ljava/lang/String;)V

??????46: aload_1

??????47:?monitorexit

??????48: goto??????????56

??????51: astore_2

??????52: aload_1

??????53:?monitorexit

??????54: aload_2

??????55: athrow

??????56: return

????Exception table:

???????from????to??target type

??????????16????48????51???any

??????????51????54????51???any

}

通過字節碼可以看出包含一個monitorenter指令以及多個monitorexit指令。這是因為jvm需要確保所獲得的鎖在正常執行路徑,以及異常執行路徑上都能夠被解鎖。

?

我們可以抽象的理解為每個鎖對象擁有一個鎖計數器和一個指向持有該鎖的線程的指針

  • 當執行 monitorenter 時,如果目標鎖對象的計數器為 0,那么說明它沒有被其他線程所持有。在這個情況下,Java 虛擬機會將該鎖對象的持有線程設置為當前線程,并且將其計數器加 1。
  • 在目標鎖對象的計數器不為 0 的情況下,如果鎖對象的持有線程是當前線程,那么 Java 虛擬機可以將其計數器加 1,否則需要等待,直至持有線程釋放該鎖。當執行 monitorexit 時,Java 虛擬機則需將鎖對象的計數器減 1。當計數器減為 0 時,那便代表該鎖已經被釋放掉了。
  • 之所以采用這種計數器的方式,是為了允許同一個線程重復獲取同一把鎖。舉個例子,如果一個 Java 類中擁有多個 synchronized 方法,那么這些方法之間的相互調用,不管是直接的還是間接的,都會涉及對同一把鎖的重復加鎖操作。因此,我們需要設計這么一個可重入的特性,來避免編程里的隱式約束。

??

我們來看一個案例:在不加鎖的情況多下通過取兩次數值然后進行對比,來模擬兩次共享狀態的操作:

java代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

public?class?DemoExample3 {

????public?int?sharedState;

?

????public?void?nonSafeAction() {

????????while?(sharedState <?100000) {

????????????int?former = sharedState++;

????????????int?latter = sharedState;

????????????if?(former != latter -?1) {

????????????????System.out.println("Observed data race, former is "?+

????????????????????????former +?", "?+?"latter is "?+ latter);

????????????}

????????}

????}

??

????public?static?void?main(String[] args)?throws?InterruptedException {

????????final?DemoExample3 demoExample3 =?new?DemoExample3();

????????Thread thread1 =?new?Thread() {

????????????@Override

????????????public?void?run() {

????????????????demoExample3.nonSafeAction();

????????????}

????????};

???

????????Thread thread2 =?new?Thread() {

????????????@Override

????????????public?void?run() {

????????????????demoExample3.nonSafeAction();

????????????}

????????};

?

????????thread1.start();

????????thread2.start();

????????thread1.join();

????????thread2.join();

????}

}

?

在沒有加?synchronized?關鍵字的時候打印出來的結果(截取部分):

Observed data race, former is 55179, latter is 55181

Observed data race, former is?56752, latter is?56754

Observed data race, former is 58304, latter is 58306

Observed data race, former is 60340, latter is 60342

Observed data race, former is 61627, latter is 61629

Observed data race, former is 63107, latter is 62946

Observed data race, former is 64029, latter is 64029

Observed data race, former is 65579, latter is 65581

Observed data race, former is 67315, latter is 67317

Observed data race, former is 68542, latter is 68542

Observed data race, former is 70687, latter is 70687

Observed data race, former is 72654, latter is 72656

Observed data race, former is 74644, latter is 74646

就會發現,打印出好多與if (former != latter - 1) 條件相符的值,這是錯誤的,正確的結果應該是一條也沒有;

?

我們在來看一下加上synchronized關鍵字的代碼:

java代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

public?class?DemoExample3 {

????public?int?sharedState;

???

????public?void?nonSafeAction() {

????????while?(sharedState <?100000) {

????????????synchronized?(this) {

????????????????int?former = sharedState++;

????????????????int?latter = sharedState;

????????????????if?(former != latter -?1) {

????????????????????System.out.println("Observed data race, former is "?+

????????????????????????????former +?", "?+?"latter is "?+ latter);

????????????????}

????????????}

????????}

????}

???

????public?static?void?main(String[] args)?throws?InterruptedException {

????????final?DemoExample3 demoExample3 =?new?DemoExample3();

????????Thread thread1 =?new?Thread() {

????????????@Override

????????????public?void?run() {

????????????????demoExample3.nonSafeAction();

????????????}

????????};

???

????????Thread thread2 =?new?Thread() {

????????????@Override

????????????public?void?run() {

????????????????demoExample3.nonSafeAction();

????????????}

????????};

????????thread1.start();

????????thread2.start();

????????thread1.join();

????????thread2.join();

????}

}

?

這次看下加上synchronized關鍵字的打印出來的結果:

Process finished with exit code 0

?

說明將兩次賦值過程用synchronized保護起來,使用this作為互斥單元,就可以避免別的線程并發的去修改?sharedState;這也就是我剛開說的并發情況下為了保證線程的安全性,我們可以通過加鎖來保證。

?


說完我們為什么需要鎖,接下來我們介紹偏向鎖、輕量級鎖、重量級鎖及鎖的膨脹過程

?

首先我們先從jvm源碼中來分析鎖的膨脹過程(鎖升級的過程):

在jvm中synchronized的是行為是jvm runntime的一部分,所以我們需要先找到?Runtime 相關的功能實現。通過在代碼中查詢類似“monitor_enter”或“Monitor Enter”,很直觀的就可以定位到:

sharedRuntime.cpp(http://hg.openjdk.java.net/jdk/jdk/file/6659a8f57d78/src/hotspot/share/runtime/sharedRuntime.cpp),它是解釋器和編譯器運行時的基類:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

// Handles the uncommon case in locking, i.e., contention or an inflated lock.

JRT_BLOCK_ENTRY(void, SharedRuntime::complete_monitor_locking_C(oopDesc* _obj, BasicLock* lock, JavaThread*?thread))

??// Disable ObjectSynchronizer::quick_enter() in default config

??// on AARCH64 and ARM until JDK-8153107 is resolved.

??if?(ARM_ONLY((SyncFlags & 256) != 0 &&)

??????AARCH64_ONLY((SyncFlags & 256) != 0 &&)

??????!SafepointSynchronize::is_synchronizing()) {

????// Only try quick_enter() if we're not trying to reach a safepoint

????// so that the calling thread reaches the safepoint more quickly.

????if?(ObjectSynchronizer::quick_enter(_obj,?thread, lock))?return;

??}

??// NO_ASYNC required because an async exception on the state transition destructor

??// would leave you with the lock held and it would never be released.

??// The normal monitorenter NullPointerException is thrown without acquiring a lock

??// and the model is that an exception implies the method failed.

??JRT_BLOCK_NO_ASYNC

??oop obj(_obj);

??if?(PrintBiasedLockingStatistics) {

????Atomic::inc(BiasedLocking::slow_path_entry_count_addr());

??}

??Handle h_obj(THREAD, obj);

??//在 JVM 啟動時,我們可以指定是否開啟偏向鎖

??if?(UseBiasedLocking) {

??// Retry fast entry if bias is revoked to avoid unnecessary inflation

?<strong>?//fast_enter 是我們熟悉的完整鎖獲取路徑</strong>

????ObjectSynchronizer::fast_enter(h_obj, lock,?true, CHECK);

??}?else?{

??//slow_enter 則是繞過偏向鎖,直接進入輕量級鎖獲取邏輯

????ObjectSynchronizer::slow_enter(h_obj, lock, CHECK);

??}

??assert(!HAS_PENDING_EXCEPTION,?"Should have no exception here");

??JRT_BLOCK_END

JRT_END

?

synchronizer.cpp(https://hg.openjdk.java.net/jdk/jdk/file/896e80158d35/src/hotspot/share/runtime/synchronizer.cpp),JVM 同步相關的各種基礎(不僅僅是 synchronized 的邏輯,包括從本地代碼,也就是 JNI,觸發的 Monitor 動作,全都可以在里面找到例如(jni_enter/jni_exit)):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

// -----------------------------------------------------------------------------

//? Fast Monitor Enter/Exit

// This the fast monitor enter. The interpreter and compiler use

// some assembly copies of this code. Make sure update those code

// if the following function is changed. The implementation is

// extremely sensitive to race condition. Be careful.

void?ObjectSynchronizer::fast_enter(Handle obj, BasicLock* lock,

????????????????????????????????????bool?attempt_rebias, TRAPS) {

??if?(UseBiasedLocking) {

????if?(!SafepointSynchronize::is_at_safepoint()) {

??????//biasedLocking定義了偏向鎖相關操作,revoke_and_rebias revokeatsafepoint 則定義了當檢測到安全點時的處理

??????BiasedLocking::Condition cond = BiasedLocking::revoke_and_rebias(obj, attempt_rebias, THREAD);

??????if?(cond == BiasedLocking::BIAS_REVOKED_AND_REBIASED) {

????????return;

??????}

????}?else?{

??????assert(!attempt_rebias,?"can not rebias toward VM thread");

??????BiasedLocking::revoke_at_safepoint(obj);

????}

????assert(!obj->mark()->has_bias_pattern(),?"biases should be revoked by now");

??}

??//如果獲取偏向鎖失敗,則進入 slow_enter,鎖升級

??slow_enter(obj, lock, THREAD);

}

??

// -----------------------------------------------------------------------------

// Interpreter/Compiler Slow Case

// This routine is used to handle interpreter/compiler slow case

// We don't need to use fast path here, because it must have been

// failed in the interpreter/compiler code.

void?ObjectSynchronizer::slow_enter(Handle obj, BasicLock* lock, TRAPS) {

??markOop mark = obj->mark();

??assert(!mark->has_bias_pattern(),?"should not see bias pattern here");

??if?(mark->is_neutral()) {

????// Anticipate successful CAS -- the ST of the displaced mark must

????// be visible <= the ST performed by the CAS.

????// 將目前的 Mark Word 復制到 Displaced Header 上

????lock->set_displaced_header(mark);

????// 利用 CAS 設置對象的 Mark Wo

????if?(mark == obj()->cas_set_mark((markOop) lock, mark)) {

??????return;

????}

????// Fall through to inflate() …

????// 檢查存在競爭

??}?else?if?(mark->has_locker() &&

?????????????THREAD->is_lock_owned((address)mark->locker())) {

????assert(lock != mark->locker(),?"must not re-lock the same lock");

????assert(lock != (BasicLock*)obj->mark(), "don't relock with same BasicLock”);

????// 清除

????lock->set_displaced_header(NULL);

????return;

??}

??// The object header will never be displaced to this lock,

??// so it does not matter what the value is, except that it

??// must be non-zero to avoid looking like a re-entrant lock,

??// and must not look locked either.

??// 重置 Displaced Header

??lock->set_displaced_header(markOopDesc::unused_mark());

??//鎖膨脹

??ObjectSynchronizer::inflate(THREAD,

??????????????????????????????obj(),

??????????????????????????????inflate_cause_monitor_enter)->enter(THREAD);

}

// This routine is used to handle interpreter/compiler slow case

// We don't need to use fast path here, because it must have

// failed in the interpreter/compiler code. Simply use the heavy

// weight monitor should be ok, unless someone find otherwise.

void?ObjectSynchronizer::slow_exit(oop object, BasicLock* lock, TRAPS) {

??fast_exit(object, lock, THREAD);

}

??

//鎖膨脹

ObjectMonitor * ATTR ObjectSynchronizer::inflate (Thread * Self, oop object) {

??// Inflate mutates the heap ...

??// Relaxing assertion for bug 6320749.

??assert?(Universe::verify_in_progress() ||

??????????!SafepointSynchronize::is_at_safepoint(),?"invariant") ;

??

??

??for?(;;) {//自旋

??????const?markOop mark = object->mark() ;

??????assert?(!mark->has_bias_pattern(),?"invariant") ;

??

??????// The mark can be in one of the following states:

??????// *? Inflated???? - just return

??????// *? Stack-locked - coerce it to inflated

??????// *? INFLATING??? - busy wait for conversion to complete

??????// *? Neutral????? - aggressively inflate the object.

??????// *? BIASED?????? - Illegal.? We should never see this

???

??????// CASE: inflated已膨脹,即重量級鎖

??????if?(mark->has_monitor()) {//判斷當前是否為重量級鎖

??????????ObjectMonitor * inf = mark->monitor() ;//獲取指向ObjectMonitor的指針

??????????assert?(inf->header()->is_neutral(),?"invariant");

??????????assert?(inf->object() == object,?"invariant") ;

??????????assert?(ObjectSynchronizer::verify_objmon_isinpool(inf),?"monitor is invalid");

??????????return?inf ;

??????}

???

??????// CASE: inflation in progress - inflating over a stack-lock.膨脹等待(其他線程正在從輕量級鎖轉為膨脹鎖)

??????// Some other thread is converting from stack-locked to inflated.

??????// Only that thread can complete inflation -- other threads must wait.

??????// The INFLATING value is transient.

??????// Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.

??????// We could always eliminate polling by parking the thread on some auxiliary list.

??????if?(mark == markOopDesc::INFLATING()) {

?????????TEVENT (Inflate: spin?while?INFLATING) ;

?????????ReadStableMark(object) ;

?????????continue?;

??????}

???

??????// CASE: stack-locked棧鎖(輕量級鎖)

??????// Could be stack-locked either by this thread or by some other thread.

??????//

??????// Note that we allocate the objectmonitor speculatively, _before_ attempting

??????// to install INFLATING into the mark word.? We originally installed INFLATING,

??????// allocated the objectmonitor, and then finally STed the address of the

??????// objectmonitor into the mark.? This was correct, but artificially lengthened

??????// the interval in which INFLATED appeared in the mark, thus increasing

??????// the odds of inflation contention.

??????//

??????// We now use per-thread private objectmonitor free lists.

??????// These list are reprovisioned from the global free list outside the

??????// critical INFLATING...ST interval.? A thread can transfer

??????// multiple objectmonitors en-mass from the global free list to its local free list.

??????// This reduces coherency traffic and lock contention on the global free list.

??????// Using such local free lists, it doesn't matter if the omAlloc() call appears

??????// before or after the CAS(INFLATING) operation.

??????// See the comments in omAlloc().

??

??????if?(mark->has_locker()) {

??????????ObjectMonitor * m = omAlloc (Self) ;//獲取一個可用的ObjectMonitor

??????????// Optimistically prepare the objectmonitor - anticipate successful CAS

??????????// We do this before the CAS in order to minimize the length of time

??????????// in which INFLATING appears in the mark.

??????????m->Recycle();

??????????m->_Responsible? = NULL ;

??????????m->OwnerIsThread = 0 ;

??????????m->_recursions?? = 0 ;

??????????m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;???// Consider: maintain by type/class

???

??????????markOop cmp = (markOop) Atomic::cmpxchg_ptr (markOopDesc::INFLATING(), object->mark_addr(), mark) ;

??????????if?(cmp != mark) {//CAS失敗//CAS失敗,說明沖突了,自旋等待//CAS失敗,說明沖突了,自旋等待//CAS失敗,說明沖突了,自旋等待

?????????????omRelease (Self, m,?true) ;//釋放監視器鎖

?????????????continue?;???????// Interference -- just retry

??????????}

??

??????????// We've successfully installed INFLATING (0) into the mark-word.

??????????// This is the only case where 0 will appear in a mark-work.

??????????// Only the singular thread that successfully swings the mark-word

??????????// to 0 can perform (or more precisely, complete) inflation.

??????????//

??????????// Why do we CAS a 0 into the mark-word instead of just CASing the

??????????// mark-word from the stack-locked value directly to the new inflated state?

??????????// Consider what happens when a thread unlocks a stack-locked object.

??????????// It attempts to use CAS to swing the displaced header value from the

??????????// on-stack basiclock back into the object header.? Recall also that the

??????????// header value (hashcode, etc) can reside in (a) the object header, or

??????????// (b) a displaced header associated with the stack-lock, or (c) a displaced

??????????// header in an objectMonitor.? The inflate() routine must copy the header

??????????// value from the basiclock on the owner's stack to the objectMonitor, all

??????????// the while preserving the hashCode stability invariants.? If the owner

??????????// decides to release the lock while the value is 0, the unlock will fail

??????????// and control will eventually pass from slow_exit() to inflate.? The owner

??????????// will then spin, waiting for the 0 value to disappear.?? Put another way,

??????????// the 0 causes the owner to stall if the owner happens to try to

??????????// drop the lock (restoring the header from the basiclock to the object)

??????????// while inflation is in-progress.? This protocol avoids races that might

??????????// would otherwise permit hashCode values to change or "flicker" for an object.

??????????// Critically, while object->mark is 0 mark->displaced_mark_helper() is stable.

??????????// 0 serves as a "BUSY" inflate-in-progress indicator

??????????// fetch the displaced mark from the owner's stack.

??????????// The owner can't die or unwind past the lock while our INFLATING

??????????// object is in the mark.? Furthermore the owner can't complete

??????????// an unlock on the object, either.

??????????markOop dmw = mark->displaced_mark_helper() ;

??????????assert?(dmw->is_neutral(),?"invariant") ;

??????????//CAS成功,設置ObjectMonitor的_header、_owner和_object等

??????????// Setup monitor fields to proper values -- prepare the monitor

??????????m->set_header(dmw) ;

?

??????????// Optimization: if the mark->locker stack address is associated

??????????// with this thread we could simply set m->_owner = Self and

??????????// m->OwnerIsThread = 1. Note that a thread can inflate an object

??????????// that it has stack-locked -- as might happen in wait() -- directly

??????????// with CAS.? That is, we can avoid the xchg-NULL .... ST idiom.

??????????m->set_owner(mark->locker());

??????????m->set_object(object);

??????????// TODO-FIXME: assert BasicLock->dhw != 0.

??

??????????// Must preserve store ordering. The monitor state must

??????????// be stable at the time of publishing the monitor address.

??????????guarantee (object->mark() == markOopDesc::INFLATING(),?"invariant") ;

??????????object->release_set_mark(markOopDesc::encode(m));

???

??????????// Hopefully the performance counters are allocated on distinct cache lines

??????????// to avoid false sharing on MP systems ...

??????????if?(ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;

??????????TEVENT(Inflate: overwrite stacklock) ;

??????????if?(TraceMonitorInflation) {

????????????if?(object->is_instance()) {

??????????????ResourceMark rm;

??????????????tty->print_cr("Inflating object "?INTPTR_FORMAT?" , mark "?INTPTR_FORMAT?" , type %s",

????????????????(void?*) object, (intptr_t) object->mark(),

????????????????object->klass()->external_name());

????????????}

??????????}

??????????return?m ;

??????}

???

??????// CASE: neutral 無鎖

??????// TODO-FIXME: for entry we currently inflate and then try to CAS _owner.

??????// If we know we're inflating for entry it's better to inflate by swinging a

??????// pre-locked objectMonitor pointer into the object header.?? A successful

??????// CAS inflates the object *and* confers ownership to the inflating thread.

??????// In the current implementation we use a 2-step mechanism where we CAS()

??????// to inflate and then CAS() again to try to swing _owner from NULL to Self.

??????// An inflateTry() method that we could call from fast_enter() and slow_enter()

??????// would be useful.

??

??????assert?(mark->is_neutral(),?"invariant");

??????ObjectMonitor * m = omAlloc (Self) ;

??????// prepare m for installation - set monitor to initial state

??????m->Recycle();

??????m->set_header(mark);

??????m->set_owner(NULL);

??????m->set_object(object);

??????m->OwnerIsThread = 1 ;

??????m->_recursions?? = 0 ;

??????m->_Responsible? = NULL ;

??????m->_SpinDuration = ObjectMonitor::Knob_SpinLimit ;???????// consider: keep metastats by type/class

???

??????if?(Atomic::cmpxchg_ptr (markOopDesc::encode(m), object->mark_addr(), mark) != mark) {

??????????m->set_object (NULL) ;

??????????m->set_owner? (NULL) ;

??????????m->OwnerIsThread = 0 ;

??????????m->Recycle() ;

??????????omRelease (Self, m,?true) ;

??????????m = NULL ;

??????????continue?;

??????????// interference - the markword changed - just retry.

??????????// The state-transitions are one-way, so there's no chance of

??????????// live-lock -- "Inflated" is an absorbing state.

??????}

???

??????// Hopefully the performance counters are allocated on distinct

??????// cache lines to avoid false sharing on MP systems ...

??????if?(ObjectMonitor::_sync_Inflations != NULL) ObjectMonitor::_sync_Inflations->inc() ;

??????TEVENT(Inflate: overwrite neutral) ;

??????if?(TraceMonitorInflation) {

????????if?(object->is_instance()) {

??????????ResourceMark rm;

??????????tty->print_cr("Inflating object "?INTPTR_FORMAT?" , mark "?INTPTR_FORMAT?" , type %s",

????????????(void?*) object, (intptr_t) object->mark(),

????????????object->klass()->external_name());

????????}

??????}

??????return?m ;

??}

}

  

膨脹過程的實現比較復雜,大概實現過程如下:

1、整個膨脹過程在自旋下完成;

2、mark->has_monitor()方法判斷當前是否為重量級鎖,即Mark Word的鎖標識位為?10,如果當前狀態為重量級鎖,執行步驟(3),否則執行步驟(4);

3、mark->monitor()方法獲取指向ObjectMonitor的指針,并返回,說明膨脹過程已經完成;

4、如果當前鎖處于膨脹中,說明該鎖正在被其它線程執行膨脹操作,則當前線程就進行自旋等待鎖膨脹完成,這里需要注意一點,雖然是自旋操作,但不會一直占用cpu資源,每隔一段時間會通過os::NakedYield方法放棄cpu資源,或通過park方法掛起;如果其他線程完成鎖的膨脹操作,則退出自旋并返回;

5、如果當前是輕量級鎖狀態,即鎖標識位為?00,膨脹過程如下:

  • ? ? 通過omAlloc方法,獲取一個可用的ObjectMonitor monitor,并重置monitor數據;
  • ? ? 通過CAS嘗試將Mark Word設置為markOopDesc:INFLATING,標識當前鎖正在膨脹中,如果CAS失敗,說明同一時刻其它線程已經將Mark Word設置為markOopDesc:INFLATING,當前線程進行自旋等待膨脹完成;
  • ? ? 如果CAS成功,設置monitor的各個字段:_header、_owner和_object等,并返回;

6、如果是無鎖,重置監視器值;

?

以上就是從jvm源碼來分析鎖的膨脹過程了。


?

接下來我們案例入手開始分析偏向鎖(批量重偏向、批量撤銷)、輕量級鎖、重量級鎖及膨脹過程:

?

偏向鎖:

  • 偏向鎖是指一段同步代碼一直被一個線程所訪問,那么該線程會自動獲取鎖,降低獲取鎖的代價。

  • 在大多數情況下,鎖總是由同一線程多次獲得,不存在多線程競爭,所以出現了偏向鎖。其目標就是在只有一個線程執行同步代碼塊時能夠提高性能。

  • 當一個線程訪問同步代碼塊并獲取鎖時,會在Mark Word里存儲鎖偏向的線程ID。在線程進入和退出同步塊時不再通過CAS操作來加鎖和解鎖,而是檢測Mark Word里是否存儲著指向當前線程的偏向鎖。引入偏向鎖是為了在無多線程競爭的情況下盡量減少不必要的輕量級鎖執行路徑,因為輕量級鎖的獲取及釋放依賴多次CAS原子指令,而偏向鎖只需要在置換ThreadID的時候依賴一次CAS原子指令即可。

  • 偏向鎖只有遇到其他線程嘗試競爭偏向鎖時,持有偏向鎖的線程才會釋放鎖,線程不會主動釋放偏向鎖。偏向鎖的撤銷,需要等待全局安全點(在這個時間點上沒有字節碼正在執行),它會首先暫停擁有偏向鎖的線程,判斷鎖對象是否處于被鎖定狀態。撤銷偏向鎖后恢復到無鎖(標志位為“01”)或輕量級鎖(標志位為“00”)的狀態。

  • 偏向鎖在JDK 6及以后的JVM里是默認啟用的。可以通過JVM參數關閉偏向鎖:-XX:-UseBiasedLocking=false,關閉之后程序默認會進入輕量級鎖狀態。

?

在上篇【java并發筆記三之synchronized 偏向鎖 輕量級鎖 重量級鎖證明】說過偏向鎖在沒有禁止延遲的時候還沒加鎖之前就已經是偏向鎖了,但是加鎖完之后,退出同步代碼塊 還是偏向鎖;計算過hashcode之后就不能被偏向。

一、我們來看段代碼證明下,在沒有計算hashcode的情況下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

//創建一個啥都沒有的類:

public?class?TestDemo {}

??

public?class?DemoExample {

????static?TestDemo testDemo;

????public?static?void?main(String[] args)?throws?Exception {

????????//此處睡眠50000ms,取消jvm默認偏向鎖延遲4000ms

????????Thread.sleep(5000);

????????testDemo=?new?TestDemo();

??

????????//hash計算?

????????//testDemo.hashCode();

??

????????System.out.println("befor lock");

????????//無鎖:偏向鎖?

????????System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

??

????????synchronized?(testDemo){

????????????System.out.println("lock ing");

????????????System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

????????}

??

????????System.out.println("after lock");

????????System.out.println(ClassLayout.parseInstance(testDemo).toPrintable());

????}

}

?

運行結果:

befor lock

OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

??????0?????4????????(object header)???????????????????????????05 00 00 00 (00000101?00000000 00000000 00000000) (5)

??????4?????4????????(object header)???????????????????????????00 00 00 00 (00000000 00000000 00000000 00000000) (0)

??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

?????12?????4????????(loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

?

?

lock ing

com.boke.TestDemo object internals:

OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

??????0?????4????????(object header)???????????????????????????05 80 80 ac (00000101?10000000 10000000 10101100) (-1400864763)

??????4?????4????????(object header)???????????????????????????8d 7f 00 00 (10001101 01111111 00000000 00000000) (32653)

??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

?????12?????4????????(loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

?

?

after lock

com.boke.TestDemo object internals:

OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

??????0?????4????????(object header)???????????????????????????05 80 80 ac (00000101?10000000 10000000 10101100)?(-1400864763)

??????4?????4????????(object header)???????????????????????????8d 7f 00 00 (10001101 01111111 00000000 00000000) (32653)

??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

?????12?????4????????(loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

?

分析結果:

befor lock:綠顏色表示:雖然是偏向鎖,但是黃顏色表示沒有任何線程持有鎖(一個對象被初始化的時候是可偏向的)

lock ?ing:?綠顏色表示偏向鎖,黃顏色的表示當前線程拿到鎖

after lock:綠顏色表示偏向鎖,黃顏色的表示當前線程拿到鎖,還是偏向的狀態;(偏向鎖退出鎖后依然是偏向狀態)

?

jvm在初始化一個對象的時候,如果沒有啟用偏向鎖延遲,就會去判斷這個對象是否可以被偏向,如果可以就是偏向鎖;退出同步代碼塊 還是偏向鎖。

?

二、在對象進行hashcode計算之后就會輸出下面的結果(也就是代碼的這塊testDemo.hashCode()去掉注釋,進行hashcode運算):

befor lock

com.boke.TestDemo object internals:

OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

??????0?????4????????(object header)???????????????????????????05 00 00 00 (00000101?00000000 00000000 00000000) (5)

??????4?????4????????(object header)???????????????????????????00 00 00 00 (00000000 00000000 00000000 00000000) (0)

??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

?????12?????4????????(loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

?

lock ing

com.boke.TestDemo object internals:

OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

??????0?????4????????(object header)???????????????????????????f8 28 4b 0c (11111000?00101000 01001011 00001100)?(206252280)

??????4?????4????????(object header)???????????????????????????00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

?????12?????4????????(loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

?

after lock

com.boke.TestDemo object internals:

OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

??????0?????4????????(object header)???????????????????????????05 80 80 ac (00000101?10000000 10000000 10101100) (-1400864763)

??????4?????4????????(object header)???????????????????????????8d 7f 00 00 (10001101 01111111 00000000 00000000) (32653)

??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

?????12?????4????????(loss due to the next object alignment)

Instance size: 16 bytes

Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

?

結果顯示并不是偏向鎖了,說明對象在計算過hashcode之后就不能被偏向;

  • 具體來說,在線程進行加鎖時,如果該鎖對象支持偏向鎖,那么 Java 虛擬機會通過 CAS操作,將當前線程的地址記錄在鎖對象的標記字段之中,并且將標記字段的最后三位設置為:1 01;

  • 在接下來的運行過程中,每當有線程請求這把鎖,Java 虛擬機只需判斷鎖對象標記字段中:最后三位是否為: 1 01,是否包含當前線程的地址,以及 epoch 值是否和鎖對象的類的epoch 值相同。如果都滿足,那么當前線程持有該偏向鎖,可以直接返回;

  • ?

    這里的?epoch 值是一個什么概念呢?

    • 我們先從偏向鎖的撤銷講起。當請求加鎖的線程和鎖對象標記字段保持的線程地址不匹配時(而且 epoch 值相等,如若不等,那么當前線程可以將該鎖重偏向至自己),Java 虛擬機需要撤銷該偏向鎖。這個撤銷過程非常麻煩,它要求持有偏向鎖的線程到達安全點,再將偏向鎖替換成輕量級鎖;

    • 如果某一類鎖對象的總撤銷數超過了一個閾值(對應 jvm參數?-XX:BiasedLockingBulkRebiasThreshold,默認為 20),那么 Java 虛擬機會宣布這個類的偏向鎖失效;(這里說的就是批量重偏向)

    ? ? ? ?JVM源碼:

    1

    2

    3

    4

    5

    product(intx, BiasedLockingBulkRebiasThreshold, 20,?????????????????????? \

    ????????"Threshold of number of revocations per type to try to "??????????\

    ????????"rebias all objects in the heap of that type")??????????????????? \

    ????????range(0, max_intx)??????????????????????????????????????????????? \

    ????????constraint(BiasedLockingBulkRebiasThresholdFunc,AfterErgo)??????? \

    ?

    • 具體的做法便是在每個類中維護一個 epoch 值,你可以理解為第幾代偏向鎖。當設置偏向鎖時,Java 虛擬機需要將該 epoch 值復制到鎖對象的標記字段中;

    • 在宣布某個類的偏向鎖失效時,Java 虛擬機實則將該類的 epoch 值加 1,表示之前那一代的偏向鎖已經失效。而新設置的偏向鎖則需要復制新的 epoch 值;

    • 為了保證當前持有偏向鎖并且已加鎖的線程不至于因此丟鎖,Java 虛擬機需要遍歷所有線程的 Java 棧,找出該類已加鎖的實例,并且將它們標記字段中的 epoch 值加 1。該操作需要所有線程處于安全點狀態;

    • 如果總撤銷數超過另一個閾值(對應 jvm 參數?-XX:BiasedLockingBulkRevokeThreshold,默認值為 40),那么 Java 虛擬機會認為這個類已經不再適合偏向鎖。此時,Java 虛擬機會撤銷該類實例的偏向鎖,并且在之后的加鎖過程中直接為該類實例設置輕量級鎖(這里說的就是偏向批量撤銷)

    ?? ???

    JVM源碼:

    1

    2

    3

    4

    5

    product(intx, BiasedLockingBulkRevokeThreshold, 40,?????????????????????? \

    ????????"Threshold of number of revocations per type to permanently "?????\

    ????????"revoke biases of all objects in the heap of that type")????????? \

    ????????range(0, max_intx)??????????????????????????????????????????????? \

    ????????constraint(BiasedLockingBulkRevokeThresholdFunc,AfterErgo)??

      


    ?

    接下來我們分析兩個批量重偏向相關案例(禁止偏向鎖延遲的情況下:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0):

    ?

    案例一:

    java代碼:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    public?class?TestDemo {

    }

    public?class?DemoExample4 {

    ????public?static?void?main(String[] args)?throws?InterruptedException {

    ????????test1();

    ????}

    ???

    ???public?class?DemoExample5 {

    ????public?static?void?main(String[] args)?throws?InterruptedException {

    ????????test1();

    ????}

    ???

    ????/**

    ?????* 僅證明批量重偏向

    ?????* @throws InterruptedException

    ?????*/

    ????public??static??void?test1()?throws?InterruptedException {

    ????????List<TestDemo> list =?new?ArrayList<>();

    ????????for?(int?i =?0; i <?100; i++) {

    ????????????list.add(new?TestDemo());

    ????????}

    ????????Thread t1 =?new?Thread(()->{

    ????????????System.out.println("加鎖前 get(0) 應該是無鎖可偏向 "+ ClassLayout.parseInstance(list.get(0)).toPrintable());

    ????????????for?(TestDemo a:list? ) {

    ????????????????synchronized?(a){

    ????????????????????System.out.print("加鎖 >");

    ????????????????}

    ????????????}

    ????????????System.out.println();

    ????????????System.out.println("加鎖后 get(0) 應該是偏向鎖"+ClassLayout.parseInstance(list.get(0)).toPrintable());

    ????????????try?{

    ????????????????TimeUnit.SECONDS.sleep(1000);//這里不讓線程死,防止線程ID復用

    ????????????}?catch?(InterruptedException e) {

    ????????????????e.printStackTrace();

    ????????????}

    ????????});

    ????????t1.start();

    ????????TimeUnit.SECONDS.sleep(5);

    ????????Thread t2 =?new?Thread(()->{

    ????????????for?(int?i =?0; i <?40; i++) {

    ????????????????TestDemo a = list.get(i);

    ????????????????synchronized?(a){

    ????????????????????System.out.print("加鎖 >");

    ????????????????}

    ????????????????if?(i==18){

    ????????????????????System.out.println();

    ????????????????????System.out.println("加鎖后 get(18) 應該是無鎖(輕量級鎖釋放) "+ClassLayout.parseInstance(list.get(i)).toPrintable());

    ????????????????}

    ????????????????if?(i==19){?//開始重偏向

    ????????????????????System.out.println();

    ????????????????????System.out.println("加鎖后 get(19) 應該是偏向鎖 "+ClassLayout.parseInstance(list.get(i)).toPrintable());

    ????????????????????System.out.println("加鎖后 get(0) 應該是無鎖(輕量級鎖釋放) "+ClassLayout.parseInstance(list.get(0)).toPrintable());

    ????????????????????System.out.println("加鎖后 get(99) 應該是偏向鎖 偏向t1 "+ClassLayout.parseInstance(list.get(99)).toPrintable());

    ????????????????}

    ????????????????if?(i==20){

    ????????????????????System.out.println();

    ????????????????????System.out.println("加鎖后 get(20) 應該是偏向鎖 "+ClassLayout.parseInstance(list.get(i)).toPrintable());

    ????????????????}

    ????????????}

    ????????});

    ????????t2.start();

    ????}

    }

    ?

    運行并分析結果:

    com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 00 00 00 (00000101?00000000 00000000 00000000) (5)

    ??????4?????4????????(object header)???????????????????????????00 00 00 00 (00000000 00000000 00000000 00000000) (0)

    ??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >

    ?

    加鎖后 get(0) 應該是偏向鎖com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 30 8a 73 (00000101?00110000 10001010 01110011) (1938436101)

    ??????4?????4????????(object header)???????????????????????????c4 7f 00 00 (11000100 01111111 00000000 00000000) (32708)

    ??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >

    加鎖后 get(18) 應該是無鎖(輕量級鎖釋放)?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????01 00 00 00 (00000001?00000000 00000000 00000000) (1)

    ??????4?????4????????(object header)???????????????????????????00 00 00 00 (00000000 00000000 00000000 00000000) (0)

    ??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    加鎖 >

    加鎖后 get(19) 應該是偏向鎖?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 41 0b 75 (00000101?01000001 00001011 01110101) (1963671813)

    ??????4?????4????????(object header)???????????????????????????c4 7f 00 00 (11000100 01111111 00000000 00000000) (32708)

    ??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    加鎖后 get(0) 應該是無鎖(輕量級鎖釋放)?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????01 00 00 00 (00000001?00000000 00000000 00000000) (1)

    ??????4?????4????????(object header)???????????????????????????00 00 00 00 (00000000 00000000 00000000 00000000) (0)

    ??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    加鎖后 get(99) 應該是偏向鎖?偏向t1?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 30 8a 73 (00000101?00110000 10001010 01110011) (1938436101)

    ??????4?????4????????(object header)???????????????????????????c4 7f 00 00 (11000100 01111111 00000000 00000000) (32708)

    ??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    加鎖 >

    加鎖后 get(20) 應該是偏向鎖?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 41 0b 75 (00000101?01000001 00001011 01110101) (1963671813)

    ??????4?????4????????(object header)???????????????????????????c4 7f 00 00 (11000100 01111111 00000000 00000000) (32708)

    ??????8?????4????????(object header)???????????????????????????43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >加鎖 >

    ?

    案例二:

    java代碼:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    27

    28

    29

    30

    31

    32

    33

    34

    35

    36

    37

    38

    39

    40

    41

    42

    43

    44

    45

    46

    47

    48

    49

    50

    51

    52

    53

    54

    55

    56

    57

    58

    59

    60

    61

    62

    63

    64

    65

    66

    67

    68

    69

    70

    71

    72

    73

    74

    75

    76

    77

    public?class?TestDemo {

    }

    public?class?DemoExample7 {

    ????public?static?void?main(String[] args)?throws?Exception {

    ??

    ????????List<TestDemo> list =?new?ArrayList<>();

    ????????//初始化數據

    ????????for?(int?i =?0; i <?100; i++) {

    ????????????list.add(new?TestDemo());

    ????????}

    ???

    ????????Thread t1 =?new?Thread() {

    ????????????String name =?"1";

    ????????????public?void?run() {

    ????????????????System.out.printf(name);

    ????????????????for?(TestDemo a : list) {

    ????????????????????synchronized?(a) {

    ????????????????????????if?(a == list.get(10)) {

    ????????????????????????????System.out.println("t1 預期是偏向鎖"?+?10?+ ClassLayout.parseInstance(a).toPrintable());

    ????????????????????????}

    ????????????????????}

    ????????????????}

    ????????????????try?{

    ????????????????????Thread.sleep(100000);

    ????????????????}?catch?(InterruptedException e) {

    ????????????????????e.printStackTrace();

    ????????????????}

    ????????????}

    ????????};

    ????????t1.start();

    ????????Thread.sleep(5000);

    ????????System.out.println("main 預期是偏向鎖"?+?10?+ ClassLayout.parseInstance(list.get(10)).toPrintable());

    ???

    ????????Thread t2 =?new?Thread() {

    ????????????String name =?"2";

    ??

    ????????????public?void?run() {

    ????????????????System.out.printf(name);

    ????????????????for?(int?i =?0; i <?100; i++) {

    ????????????????????TestDemo a = list.get(i);

    ????????????????????// hack 為了在批量重偏向發生后再次加鎖,前面使用了輕量級鎖的對象

    ????????????????????if?(i ==?20) {

    ????????????????????????a = list.get(9);

    ????????????????????}

    ???

    ????????????????????synchronized?(a) {

    ????????????????????????if?(i ==?10) {

    ????????????????????????????//已經經過偏向鎖撤銷,并使用輕量級鎖的對象,釋放后? 狀態依為001 無鎖狀態

    ????????????????????????????System.out.println("t2 i=10 get(1)預期是無鎖"?+ ClassLayout.parseInstance(list.get(1)).toPrintable());

    ????????????????????????????//因為和t1交替使用對象a 沒有發生競爭,但偏向鎖已偏向,另外不滿足重偏向條件,所以使用輕量級鎖

    ????????????????????????????System.out.println("t2 i=10 get(i) 預期輕量級鎖 "?+ i + ClassLayout.parseInstance(a).toPrintable());

    ????????????????????????}

    ????????????????????????if?(i ==?19) {

    ????????????????????????????//已經經過偏向鎖撤銷,并使用輕量級鎖的對象,在批量重偏向發生后。不會影響現有的狀態? 狀態依然為001

    ????????????????????????????System.out.println("t2? i=19? get(10)預期是無鎖"?+?10?+ ClassLayout.parseInstance(list.get(10)).toPrintable());

    ????????????????????????????//滿足重偏向條件后,已偏向的對象可以重新使用偏向鎖 將線程id指向當前線程,101

    ????????????????????????????System.out.println("t2? i=19? get(i) 滿足重偏向條件20 預期偏向鎖 "?+ i + ClassLayout.parseInstance(a).toPrintable());

    ????????????????????????????//滿足重偏向條件后,已偏向還為需要加鎖的對象依然偏向線程1 因為偏向鎖的撤銷是發生在下次加鎖的時候。這里沒有執行到同步此對象,所以依然偏向t1

    ????????????????????????????System.out.println("t2? i=19? get(i) 滿足重偏向條件20 但后面的對象沒有被加鎖,所以依舊偏向t1 "?+ i + ClassLayout.parseInstance(list.get(40)).toPrintable());

    ????????????????????????}

    ????????????????????????if?(i ==?20) {

    ????????????????????????????//滿足重偏向條件后,再次加鎖之前使用了輕量級鎖的對象,依然輕量級鎖,證明重偏向這個狀態只針對偏向鎖。已經發生鎖升級的,不會退回到偏向鎖

    ????????????????????????????System.out.println("t2? i=20 滿足偏向條件之后,之前被設置為無鎖狀態的對象,不可偏向,這里使用的是輕量級鎖? get(9)預期是輕量級鎖 "?+ ClassLayout.parseInstance(a).toPrintable());

    ????????????????????????}

    ????????????????????}

    ????????????????}

    ????????????????try?{

    ????????????????????Thread.sleep(100000);

    ????????????????}?catch?(InterruptedException e) {

    ????????????????????e.printStackTrace();

    ????????????????}

    ????????????}

    ????????};

    ????????t2.start();

    ????????Thread.sleep(5000);

    ????}

    }

    ?

    運行并分析結果:

    t1 預期是偏向鎖10??com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 78 86 af (00000101?01111000 10000110 10101111) (-1350141947)

    ??????4?????4????????(object header)???????????????????????????f6 7f 00 00 (11110110 01111111 00000000 00000000) (32758)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    main 預期是偏向鎖 10?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 78 86 af (00000101?01111000 10000110 10101111) (-1350141947)

    ??????4?????4????????(object header)???????????????????????????f6 7f 00 00 (11110110 01111111 00000000 00000000) (32758)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    2t2 i=10 get(1)預期是無鎖?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????01 00 00 00 (00000001?00000000 00000000 00000000) (1)

    ??????4?????4????????(object header)???????????????????????????00 00 00 00 (00000000 00000000 00000000 00000000) (0)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    t2 i=10 get(i) 預期輕量級鎖 10?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????08 69 42 08 (00001000?01101001 01000010 00001000) (138569992)

    ??????4?????4????????(object header)???????????????????????????00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    t2??i=19??get(10)預期是無鎖10com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????01 00 00 00 (00000001?00000000 00000000 00000000) (1)

    ??????4?????4????????(object header)???????????????????????????00 00 00 00 (00000000 00000000 00000000 00000000) (0)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    t2??i=19??get(i) 滿足重偏向條件20 預期偏向鎖 19com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 71 95 ae (00000101?01110001 10010101 10101110) (-1365937915)

    ??????4?????4????????(object header)???????????????????????????f6 7f 00 00 (11110110 01111111 00000000 00000000) (32758)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    t2??i=19??get(i) 滿足重偏向條件20 但后面的對象沒有被加鎖,所以依舊偏向t1 19com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????05 78 86 af (00000101?01111000 10000110 10101111) (-1350141947)

    ??????4?????4????????(object header)???????????????????????????f6 7f 00 00 (11110110 01111111 00000000 00000000) (32758)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    t2??i=20 滿足偏向條件之后,之前被設置為無鎖狀態的對象,不可偏向,這里使用的是輕量級鎖??get(9)預期是輕量級鎖?com.boke.TestDemo object internals:

    OFFSET??SIZE???TYPE DESCRIPTION???????????????????????????????VALUE

    ??????0?????4????????(object header)???????????????????????????08 69 42 08 (00001000?01101001 01000010 00001000) (138569992)

    ??????4?????4????????(object header)???????????????????????????00 70 00 00 (00000000 01110000 00000000 00000000) (28672)

    ??????8?????4????????(object header)???????????????????????????05 d6 00 f8 (00000101 11010110 00000000 11111000) (-134162939)

    ?????12?????4????????(loss due to the next object alignment)

    Instance size: 16 bytes

    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

    ?

    ?

    總結

    以上是生活随笔為你收集整理的java并发synchronized 锁的膨胀过程(锁的升级过程)深入剖析(1)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。