点赞关注,不会迷路!
前言
干货分享——Java中的锁问题 后续解决方法更新啦!不知道你们之前有没有想到呢?快来看看吧
分析结果3:
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,然后 写一个偏向锁的例子看看结果
Java代码和输出结果:
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)
分析结果4
上面这个程序只有一个线程去调用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 }
如果调用wait方法则立刻变成重量锁
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 }
需要注意的是如果对象已经计算了 hashcode就不能偏向了
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 }