1、Synchronized基本特性回顾
应用场景:多线程环境下保证线程的安全性。
使用方式:
1)Synchronized加在普通方法上,使用的是this锁,也就是当前对象
2)Synchronized加在静态方法上,使用的是class字节码作为锁
3)Synchronized加在代码块,可以以任何对象作为锁
Synchronized锁会关联一个对象 monitor对象
Synchronized是一个Java关键字 Java对象 转 C代码 转 汇编。
2、Synchronized monitor对象源码解读
每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中就被设置指向 Monitor 对象的指针。
Synchronized的锁对象的对象头中的Mark Word会关联一个monitor对象。这个monitor对象不是我们主动创建的,是JVM的线程执行到这个同步代码块,发现锁对象没有monitor就会创建monitor,monitor内部有两个重要的成员变量:
【owner】记录拥有这把锁的线程;
【recursions】记录线程拥有锁的次数(重入次数、递归次数)。
当一个线程拥有monitor后其他线程只能等待
【_EntryList】锁池。锁的竞争过程中,一旦有线程抢锁成功,其他线程会存放在_EntryList中,处于等待阻塞状态。竞争锁失败的会存放在这里阻塞等待。
(当锁释放之后,唤醒锁池中的线程)
【_cxq】竞争,所有线程都没有拿到锁的情况下,通过单向链表的形式存放参与竞争的线程。一旦有线程抢锁成功,其他线程会存放在_EntryList中。
【_WaitSet】等待池。等待池中的线程不会去竞争锁资源。通过锁对象的.wait方法,当前线程释放所持的锁,且变为阻塞状态存放到_WaitSet;调用notify方法时,取出一个在_WaitSet中等待的线程,放入到_EntryList中(并未唤醒)。notify方法所谓的唤醒线程只是将线程转移到_EntryList中,并未真正唤醒。
3、Synchronized对象布局分析
对象头16个字节:
MarkWord 对象自身的运行时数据 :存放hashcode、GC分代、锁状态标记、持有的锁线程、偏向锁线程 ID等 8个字节。
Class Point 指向实例的引用 8个字节
实例数据(成员变量 ):
private int userId; //4个字节
对齐填充数据:
由于HotSpot虚拟机的自动内存管理系统要求对象的起始地址必须是8字节的整数倍,也就是对象的大小必须是8字节的整数倍。
当对象头加上实例数据的字节数不是8的倍数,需要通过对齐填充来补全。
4、new 一个对象占用多少个字节?
对象头16个字节(未压缩)+实例数据(int四个字节,boolean1个字节)+填充数据(凑8的倍数)。
5、Synchronized锁的升级过程
5.1偏向锁:(jdk6开始优化)
定义:只有一个线程重复使用的时候产生,通过判断减少CAS过程。(适用于一个线程)
优点:没有锁的竞争,提高锁的效率。
过程:
① 线程t1检查Java对象头中是否有关联当前线程。
② 如果没有的情况下,使用CAS修改MarkWord,标记为当前线程t1。
③ 如果使用CAS修改成功,获取锁;如果修改失败,则说明当前MarkWord已经被其他线程修改过,获取锁失败。
④ 如果使用CAS修改MarkWord成功,MarkWord标记为当前线程,同时修改MarkWord中偏向锁标记为1。
偏向锁撤销:如果线程一直持有偏向锁,突然有另一个线程开始竞争java锁对象,这时偏向锁会开始撤销。撤销之后有可能会改为轻量锁或重量锁。
5.2 轻量锁
定义:当多个线程在间隔的方式竞争我们的锁对象,短暂结合自旋控制。
栈帧:方法的执行空间
过程:
T线程会复制对锁象头中markword(gc分代、hashcode)到栈帧空间,使用CAS修改锁对象的markword(非复制过来的markword)指针关联到该线程的栈帧空间地址,修改成功执行同步代码;修改失败则自旋,自旋多次升级为重量级锁。
为甚么要升级到重量级锁? 因为自旋非常消耗内存
5.3 重量锁:
只要有一个线程获取到锁,其他线程都会变为阻塞状态。效率非常低。
6、、偏向锁、轻量级锁、自旋锁、重量级锁过程
偏向锁:只有一个线程的情况下,可以使用轻量级减少CAS加锁和释放锁的操作。如果多个线程同时访问,偏向锁会撤销为轻量锁或重量锁。
轻量锁:多个线程间隔或短暂竞争锁的情况下。不会导致当前线程阻塞,没有获取到锁的线程会采用自旋的形式重复获取锁,但是非常消耗内存,如果多次重复获取锁,则升级为重量锁。
应用场景:同步代码块里的代码执行时间是非常快的情况下。
重量锁:没有获取到锁的线程会变为阻塞状态,效率是极低。
应用场景:线程不会采用自旋的形式,不会消耗CPU资源,释放CPU执行权,同步代码块执行可能比较耗时,其他线程在自旋过程中仍未执行完同步代码块。
7、Synchronized锁的使用需要注意哪些优化问题
1)减少Synchronized同步的范围,减少同步代码块执行的时间,只会使用到偏向锁或轻量锁;
2)将锁的粒度拆分得更细;
类似Conhashmap底层实现原理 降低锁的粒度
3)锁一定要做读写分离,只是读的操作不需要加锁;
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)