點贊關注,不會迷路!
乾貨分享——Java中的鎖問題 後續解決方法更新啦!不知道你們之前有沒有想到呢?快來看看吧
1-----9行是沒有進行hashcode之前的物件頭資訊,可以看到1-7B的56bit沒有值,打 印完hashcode之後16----21行就有值了,為什麼是1-7B,不是0-6B呢?因為是小端存 儲。其中12行是我們通過hashcode方法列印的結果,13行是我根據1-7B的資訊計算出來 的hashcode,所以可以確定java物件頭當中的mark work裡面的後七個位元組儲存的是 hashcode資訊,那麼第一個位元組當中的八位分別存的就是分帶年齡、偏向鎖資訊,和物件 狀態,這個8bit分別表示的資訊如下圖(其實上圖也有資訊),這個圖會隨著物件狀態改變 而改變,下圖是無鎖狀態下
關於物件狀態一共分為五種狀態,分別是無鎖、偏向鎖、輕量鎖、重量鎖、 GC標記,那麼2bit,如何能表示五種狀態(2bit最多隻能表示4中狀態分別是: 00,01,10,11),jvm做的比較好的是把偏向鎖和無鎖狀態表示為同一個狀態, 然後根據圖中偏向鎖的標識再去標識是無鎖還是偏向鎖狀態。什麼意思呢?寫個 程式碼分析一下,在寫程式碼之前我們先記得無鎖狀態下的資訊00000001,然後 寫一個偏向鎖的例子看看結果
1 package com.luban.layout;
2 import org.openjdk.jol.info.ClassLayout;
3 import static java.lang.System.out;
4
5 public class JOLExample2 {
6 static A a;
7 public static void main(String[] args) throws Exception {
8 //Thread.sleep(5000);
9 a = new A();
10 out.println("befre lock");
11 out.println(ClassLayout.parseInstance(a).toPrintable());
12 sync();
13 out.println("after lock");
14 out.println(ClassLayout.parseInstance(a).toPrintable());
15 }
16
17 public static void sync() throws InterruptedException {
18 synchronized (a){
19 System.out.println("我也不知道要列印什麼");
20 }
21
22 }
23 }
1 befre lock
2 com.luban.layout.A object internals:
3 OFFSET SIZE TYPE DESCRIPTION VALUE
4 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
5 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
6 8 4 (object header) 43 c1 00 20 (01000011 11000001 00000000 00100000) (5 36920387)
7 12 1 boolean A.flag false
8 13 3 (loss due to the next object alignment)
9 Instance size: 16 bytes
10 Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
11
12 我也不知道要列印什麼
13 after lock
14 com.luban.layout.A object internals:
15 OFFSET SIZE TYPE DESCRIPTION VALUE
16 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
17 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
18 8 4 (object header) 43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
19 12 1 boolean A.flag false
20 13 3 (loss due to the next object alignment)
上面這個程式只有一個執行緒去呼叫sync方法,故而講道理應該是偏向鎖,但 是你會發現輸出的結果(第一個位元組)依然是00000001和無鎖的時候一模一 樣,其實這是因為虛擬機器器在啟動的時候對於偏向鎖有延遲,比如把上述程式碼當中 的睡眠註釋掉結果就會不一樣了,結果會變成00000101當然為了方便測試我 們可以直接通過JVM的引數來禁用延遲-XX:+UseBiasedLocking - XX:BiasedLockingStartupDelay=0,想想為什麼偏向鎖會延遲?(除了這 8bit,其他bit儲存了執行緒資訊和epoch,這裡不截圖了),需要注意的after lock,退出同步後依然保持了偏向資訊
1 package com.luban.layout;
2 public class A {
3 int i;
4 public synchronized void parse(){
5 i++;
6 }
7 }
1 package com.luban.layout;
2 import org.openjdk.jol.info.ClassLayout;
3 import static java.lang.System.out;
4 //‐XX:BiasedLockingStartupDelay=20000 ‐XX:BiasedLockingStartupDelay=0
5 public class JOLExample3 {
6 public static void main(String[] args) throws Exception {
7 A a = new A();
8 long start = System.currentTimeMillis();
9 //呼叫同步方法1000000000L 來計算1000000000L的++,對比偏向鎖和輕量級鎖的效能
10 //如果不出意外,結果灰常明顯
11 for(int i=0;i<1000000000L;i++){
12 a.parse();
13 }
14 long end = System.currentTimeMillis();
15 System.out.println(String.format("%sms", end ‐ start));
16
17 }
18
19 }
那麼問題來了,什麼是輕量級鎖呢?工作原理是什麼呢?為什麼比偏向鎖 慢?輕量級鎖嘗試在應用層面解決執行緒同步問題,而不觸發作業系統的互斥操 作,輕量級鎖減少多執行緒進入互斥的機率,不能代替互斥
1 package com.luban.layout;
2 import org.openjdk.jol.info.ClassLayout;
3 import static java.lang.System.out;
4
5 public class JOLExample5 {
6 static A a;
7 public static void main(String[] args) throws Exception {
8 a = new A();
9 out.println("befre lock");
10 out.println(ClassLayout.parseInstance(a).toPrintable());
11 sync();
12 out.println("after lock");
13 out.println(ClassLayout.parseInstance(a).toPrintable());
14 }
15
16 public static void sync() throws InterruptedException {
17 synchronized (a){
18 out.println("lock ing");
19 out.println(ClassLayout.parseInstance(a).toPrintable());
20 }
21 }
22 }
1 package com.luban.layout;
2
3 import java.util.concurrent.CountDownLatch;
4
5 public class JOLExample4 {
6 static CountDownLatch countDownLatch = new CountDownLatch(1000000000);
7 public static void main(String[] args) throws Exception {
8 final A a = new A();
9
10 long start = System.currentTimeMillis();
11
12 //呼叫同步方法1000000000L 來計算1000000000L的++,對比偏向鎖和輕量級鎖的效能
13 //如果不出意外,結果灰常明顯
14 for(int i=0;i<2;i++){
15 new Thread(){
16 @Override
17 public void run() {
18 while (countDownLatch.getCount() > 0) {
19 a.parse();
20 }
21 }
22 }.start();
23 }
24 countDownLatch.await();
25 long end = System.currentTimeMillis();
26 System.out.println(String.format("%sms", end ‐ start));
27
28 }
29
30 }
1 package com.luban.layout;
2 import org.openjdk.jol.info.ClassLayout;
3 import static java.lang.System.out;
4
5 public class JOLExample6 {
6 static A a;
7 public static void main(String[] args) throws Exception {
8 //Thread.sleep(5000);
9 a = new A();
10 out.println("befre lock");
11 out.println(ClassLayout.parseInstance(a).toPrintable());
12
13 Thread t1= new Thread(){
14 public void run() {
15 synchronized (a){
16 try {
17 Thread.sleep(5000);
18 System.out.println("t1 release");
19 } catch (InterruptedException e) {
20 e.printStackTrace();
21 }
22 }
23 }
24 };
25 t1.start();
26 Thread.sleep(1000);
27 out.println("t1 lock ing");
28 out.println(ClassLayout.parseInstance(a).toPrintable());
29 sync();
30 out.println("after lock");
31 out.println(ClassLayout.parseInstance(a).toPrintable());
32
33 System.gc();
34 out.println("after gc()");
35 out.println(ClassLayout.parseInstance(a).toPrintable());
36 }
37
38 public static void sync() throws InterruptedException {
39 synchronized (a){
40 System.out.println("t1 main lock");
41 out.println(ClassLayout.parseInstance(a).toPrintable());
42 }
43 }
44 }
1 package com.luban.layout;
2
3 import org.openjdk.jol.info.ClassLayout;
4
5 import static java.lang.System.out;
6
7 public class JOLExample7 {
8 static A a;
9 public static void main(String[] args) throws Exception {
10 //Thread.sleep(5000);
11 a = new A();
12 out.println("befre lock");
13 out.println(ClassLayout.parseInstance(a).toPrintable());
14
15 Thread t1= new Thread(){
16 public void run() {
17 synchronized (a){
18 try {
19 synchronized (a) {
20 System.out.println("before wait");
21 out.println(ClassLayout.parseInstance(a).toPrintable());
22 a.wait();
23 System.out.println(" after wait");
24 out.println(ClassLayout.parseInstance(a).toPrintable());
25 }
26 } catch (InterruptedException e) {
27 e.printStackTrace();
28 }
29 }
30 }
31 };
32 t1.start();
33 Thread.sleep(5000);
34 synchronized (a) {
35 a.notifyAll();
36 }
37 }
38 }
1 package com.luban.layout;
2 import org.openjdk.jol.info.ClassLayout;
3 import static java.lang.System.out;
4
5 public class JOLExample8 {
6 static A a;
7 public static void main(String[] args) throws Exception {
8
9 Thread.sleep(5000);
10 a = new A();
11 a.hashCode();
12 out.println("befre lock");
13 out.println(ClassLayout.parseInstance(a).toPrintable());
14
15 Thread t1= new Thread(){
16 public void run() {
17 synchronized (a){
18 try {
19 synchronized (a) {
20 System.out.println("lock ed");
21 out.println(ClassLayout.parseInstance(a).toPrintable());
22 }
23 } catch (Exception e) {
24 e.printStackTrace();
25 }
26 }
27 }
28 };
29 t1.start();
30
31 }
32 }