synchronized
2020-05-20 11:11 Tony、 阅读(221) 评论(0) 编辑 收藏 举报
synchronized的使用方式有两种
1 对一个对象进行加锁
synchronized(O){ //代码 }
2 对一个方法加锁
public synchornized void func(){ //代码 }
其实无论是对一个对象进行加锁还是对一个方法进行加锁,实际上,都是对对象进行加锁。
java中一个对象由三部分组成=>
1 对象头=>markwork(hashCode(只有在调用后才会有),GC分代年龄,锁信息),class指针,如果是数组的话还会有一个数组的长度
2 实例数据=》存放对象字段的数据
3 填充数据=》为了保证一个对象所占用的字节数是8的倍数,提高执行效率
4 如果对象是数组的话对象头
锁的升级过程
1 普通对象-》轻量级锁
偏向锁未启动,轻度竞争会进入轻量级锁
package com.my; import org.openjdk.jol.info.ClassLayout; /** * Hello world! */ public class App01 { public static void main(String[] args) throws InterruptedException { Object o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); Thread thread = new Thread(()->{ synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } }); thread.start(); thread.join(); } }
java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) f8 f4 77 1c (11111000 11110100 01110111 00011100) (477623544) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
2 匿名偏向-》偏向锁
JVM启动4秒(默认)之后创建的对象 是匿名偏向,加锁之后会进入偏向锁。偏向锁就是在匿名偏向的基础上,将当前线程ID放入markwork
package com.my; import org.openjdk.jol.info.ClassLayout; /** * Hello world! */ public class App02 { public static void main(String[] args) throws InterruptedException { Thread.sleep(5000); Object o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } } }
java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 38 d1 02 (00000101 00111000 11010001 00000010) (47265797) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
3 偏向锁=》轻量级锁
偏向锁轻度竞争会进入轻量级锁
package com.my; import org.openjdk.jol.info.ClassLayout; /** * Hello world! */ public class App02 { private static Object o; public static void test() { synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } } public static void main(String[] args) throws InterruptedException { Thread.sleep(5000); o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); test(); new Thread(() -> { test(); }).start(); } }
java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 39 6f 02 (00000101 00111001 01101111 00000010) (40843525) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) a8 ef c7 1b (10101000 11101111 11000111 00011011) (466087848) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
4 偏向锁=》重量级锁
4.1
竞争加剧会进入重量级锁
竞争加剧:有线程超过10次自旋, -XX:PreBlockSpin, 或者自旋线程数超过CPU核数的一半, 1.6之后,加入自适应自旋 Adapative Self Spinning , JVM自己控制
package com.my; import org.openjdk.jol.info.ClassLayout; /** * Hello world! */ public class App03 { private static Object o; public static void test() { synchronized (o) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ClassLayout.parseInstance(o).toPrintable()); } } public static void main(String[] args) throws InterruptedException { Thread.sleep(5000); o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); test(); for (int i = 0; i < 2; i++) { new Thread(() -> { test(); }).start(); } } }
java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 38 69 03 (00000101 00111000 01101001 00000011) (57227269) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 5a 76 78 03 (01011010 01110110 01111000 00000011) (58226266) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 5a 76 78 03 (01011010 01110110 01111000 00000011) (58226266) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
4.2
await 会进入重量级级锁
package com.my; import org.openjdk.jol.info.ClassLayout; /** * Hello world! */ public class App04 { private static Object o; public static void test() { synchronized (o) { try { o.wait(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(ClassLayout.parseInstance(o).toPrintable()); } } public static void main(String[] args) throws InterruptedException { Thread.sleep(5000); o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); test(); } }
java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 9a ed ee 19 (10011010 11101101 11101110 00011001) (435088794) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
5 轻量级锁-》重量级
竞争加剧会进入重量级锁
竞争加剧:有线程超过10次自旋, -XX:PreBlockSpin, 或者自旋线程数超过CPU核数的一半, 1.6之后,加入自适应自旋 Adapative Self Spinning , JVM自己控制
package com.my; import org.openjdk.jol.info.ClassLayout; /** * Hello world! */ public class App05 { private static Object o; public static void test() { synchronized (o) { System.out.println(ClassLayout.parseInstance(o).toPrintable()); } } public static void main(String[] args) throws InterruptedException { o = new Object(); System.out.println(ClassLayout.parseInstance(o).toPrintable()); test(); for (int i = 0; i < 2; i++) { new Thread(App05::test).start(); } } }
java.lang.Object 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) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 88 f4 61 02 (10001000 11110100 01100001 00000010) (39974024) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) aa a1 93 02 (10101010 10100001 10010011 00000010) (43229610) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 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.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) aa a1 93 02 (10101010 10100001 10010011 00000010) (43229610) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
偏向锁的概念一直不太好理解,偏向锁其实没有上锁,只是将线程指针加到对象头中(将线程指针加到对象头中原理是cas)。之所要偏向锁,是因为大部分情况下都是一个进程在操作,当有竞争的时候,需要将锁升级(一般升级为轻量级锁,如果加了await就直接进入重量级锁,在升为轻量级锁的时候,偏向的线程也要和其他的线程重新进行cas操作来获取锁),但是在升级的时候会让偏向的这个线程先获取锁,偏向锁的意义就是为了让偏向的线程先获取锁,如果不让偏向的线程先过去锁,那岂不是就会出现,偏向的线程锁住的同步模块还没有执行完,其他的线程就可以获得锁执行同步模块了,从而导致锁失效了。
偏向锁产生的条件?=》只有偏向锁已启动(即偏向锁的标志位为1)的对象才能产生偏向锁,如果没有则直接进入轻量级锁。
对象如何启动偏向?=》只有在jvm启动一段时间(一般4秒钟,-XX:BiasedLockingStartupDelay)后创建的对象jvm才会给该对象头的偏向锁标志位设置为1。
偏向锁有个时延,默认是4秒why?=> 因为JVM虚拟机自己有一些默认启动的线程,里面有好多sync代码,这些sync代码启动时就知道肯定会有竞争,如果使用偏向锁,就会造成偏向锁不断的进行锁撤销和锁升级的操作,效率较低
偏向锁是否一定比自旋锁效率高?
不一定,在明确知道会有多线程竞争的情况下,偏向锁肯定会涉及锁撤销,这时候直接使用自旋锁。
为什么有自旋锁还需要重量级锁?
自旋是消耗CPU资源的,如果锁的时间长,或者自旋线程多,CPU会被大量消耗,重量级锁有等待队列,所有拿不到锁的进入等待队列,不需要消耗CPU资源
可见性:
JMM关于synchronized的两条规定:
1)在线程执行的代码中遇到释放锁前,必须把工作内存中共享变量的最新值刷新到主内存中
2)在线程执行的代码中中遇到获得锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值
public class Test { private static int x = 0; private static int y = 0; public static void main(String[] args) { new Thread(() -> { while (true) { synchronized (Test.class) { } if (x == 1 & y == 2) { System.out.println("thread1 end"); break; } } }).start(); new Thread(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } x = 1; y = 2; }).start(); System.out.println("main is end"); } }