目录
偏向锁
用途
public void method1(){ synchronized(object){ method2(); } } public void method2(){ synchronized(object){ method3(); } } public void method3(){ synchronized(object){ // 执行代码 } }没有线程竞争时,持锁线程反复获取锁(锁重入);每次都会生成锁记录以及进行 CAS 操作
Java6 引入偏向锁进行优化;第一次 CAS 操作,将线程 ID 设置到对象头 Mark Word 中;之后,查看线程 ID 为自己,则表示无竞争,不进行 CAS 操作;以后,只要没有竞争,锁归该线程所有
偏向状态
一个对象创建时:
① 如果开启了偏向锁(默认开启),Mark Word 的值为 0x05 即二进制后三位 101;thread、epoch、age 均为 0
② 如果未开启,Mark Word 的值为 0x01 即二进制后三位 001;hashcode、age 均为 0
③ 偏向锁默认延迟开启,不会立即生效
BiasedLockingStartupDelay=4000 // 默认延迟 4s -XX:BiasedLockingStartupDelay=0 // 加 VM 参数来禁用延迟
偏向延迟
① 默认延迟 (001)正常无锁状态
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { MyInteger myInteger = new MyInteger(0); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } }
② 睡 5s 后(101)变为偏向锁
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { Thread.sleep(5000); MyInteger myInteger = new MyInteger(0); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } }
加锁
① 直接创建然后加锁(00)轻量级锁
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { MyInteger myInteger = new MyInteger(0); synchronized (myInteger){ log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } } }
② 睡 5s 后加锁(101)变为偏向锁
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { Thread.sleep(5000); MyInteger myInteger = new MyInteger(0); synchronized (myInteger){ log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } } }
锁撤销
调用对象 hashcode
原因:偏向锁状态下,Mark Word 要用 54 位来存线程 ID,没有位置存 hashcode
无锁状态下:存于 Mark Word 中(初始为 0)
轻量级锁:hashcode 存于锁记录中
重量级锁:hashcode 存于 monitor 中
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { MyInteger myInteger = new MyInteger(0); log.debug("--------------------------------调用 hashcode 前--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); myInteger.hashCode(); log.debug("--------------------------------调用 hashcode 后--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } }
其他线程使用该对象锁
原因:多个线程获取同一把锁,但是没有竞争,交错获取;初始为偏向锁,其他线程获取时,会将其升级为轻量级锁(一般情况)
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { MyInteger myInteger = new MyInteger(0); new Thread(() -> { synchronized (myInteger){ log.debug("--------------------------------线程 t 使用该对象锁--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } }, "t").start(); Thread.sleep(5000); log.debug("--------------------------------线程 t 使用结束--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); synchronized (myInteger){ log.debug("--------------------------------main 使用该对象锁--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } } }
wait / notify
原因:wait / notify 只有重量级锁才能使用;调用之后,升级为重量级锁
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { MyInteger myInteger = new MyInteger(0); new Thread(() -> { synchronized (myInteger){ log.debug("--------------------------------线程 t 使用该对象锁--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); log.debug("--------------------------------线程 t 调用 wait--------------------------------"); try { myInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("--------------------------------线程 t wait 结束--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } }, "t").start(); Thread.sleep(5000); synchronized (myInteger){ log.debug("--------------------------------main 使用该对象锁--------------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); log.debug("--------------------------------main 调用 notify--------------------------------"); myInteger.notify(); } } }
批量重偏向(难点)
BiasedLockingBulkRebiasThreshold=20 // 开启批量重偏向的阈值① 同一个类,它对象的偏向锁被撤销达到 20 次,则会进行批量重偏向(从第二十次开始)
② 线程第一次遇到偏向锁都会先撤销,批量重偏向不算做锁撤销
③ 如一开始,偏向锁偏向 t1,它有机会偏向其他线程;重偏向之后,保存的线程 ID 会重置
@Slf4j(topic = "c.MarkWord") public class MarkWord { public static void main(String[] args) throws InterruptedException { List<MyInteger> list = new ArrayList(); new Thread(() -> { for(int i = 0; i < 30; i++){ MyInteger myInteger = new MyInteger(); list.add(myInteger); log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); synchronized (myInteger){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } synchronized (MyInteger.class){ MyInteger.class.notify(); } }, "t1").start(); new Thread(() -> { synchronized (MyInteger.class){ try { MyInteger.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for(int i = 0; i < list.size(); i++){ log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); synchronized (list.get(i)){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } }, "t2").start(); } }1. 初始化,线程 t1 中给这 30 个对象加的都是偏向锁
2. 然后,线程 t2 获取这 30 把锁时,0 ~ 18 都升级为轻量级锁,19 ~ 29 都重新偏向 t2
3. 这样操作,在 t3 里撤销第 20 次,则 19 ~ 29 都会重新偏向 t3
@Slf4j(topic = "c.MarkWord") public class MarkWord { static Thread t1, t2, t3; public static void main(String[] args) throws InterruptedException { List<MyInteger> list = new ArrayList(); t1 = new Thread(() -> { for(int i = 0; i < 30; i++){ MyInteger myInteger = new MyInteger(); list.add(myInteger); log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); synchronized (myInteger){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } LockSupport.unpark(t2); }, "t1"); t1.start(); t2 = new Thread(() -> { LockSupport.park(); for(int i = 0; i < 19; i++){ log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); synchronized (list.get(i)){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } LockSupport.unpark(t3); }, "t2"); t2.start(); t3 = new Thread(() -> { LockSupport.park(); for(int i = 19; i < list.size(); i++){ log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); synchronized (list.get(i)){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } }, "t3"); t3.start(); } }
批量撤销(难点)
BiasedLockingBulkRevokeThreshold=40 // 开启批量撤销的阈值 BiasedLockingDecayTime=25000 // 达到阈值就马上进行批量撤销的时间范围① 锁撤销次数达到 40 次并且在 25s 内,则开始批量撤销
② 批量撤销:整个类的对象都不可偏向,包括新建的对象
③ 时间 >= 25s 时,重置在 [20, 40) 内的次数,可起到再次批量重偏向的作用
1. 重偏向时,不算做撤销;如:即使到第 100 把锁,都还是重偏向
2.1. 创建 39 把锁(开始重偏向只撤销了 19 把锁,第 20 把还是偏向锁,还剩 20 把偏向锁);
2.2. 由之前的案例可得:0 ~ 18 在 t2 中升级为轻量级锁,19 ~ 38 仍然为偏向锁;
2.3. t3 中,0 ~ 18 不会影响撤销数(已经为轻量级锁),19 ~ 38 依次被撤销,撤销数达到 40 开始批量撤销;新对象初始态为无锁状态
@Slf4j(topic = "c.MarkWord") public class MarkWord { static Thread t1, t2, t3; public static void main(String[] args) throws InterruptedException { List<MyInteger> list = new ArrayList(); t1 = new Thread(() -> { for(int i = 0; i < 39; i++){ MyInteger myInteger = new MyInteger(); list.add(myInteger); log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); synchronized (myInteger){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(myInteger).toPrintable()); } LockSupport.unpark(t2); }, "t1"); t1.start(); t2 = new Thread(() -> { LockSupport.park(); for(int i = 0; i < list.size(); i++){ log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); synchronized (list.get(i)){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } LockSupport.unpark(t3); }, "t2"); t2.start(); t3 = new Thread(() -> { LockSupport.park(); for(int i = 0; i < list.size(); i++){ log.debug(i + "---------------------------加锁前---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); synchronized (list.get(i)){ log.debug(i + "---------------------------加锁中---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } log.debug(i + "---------------------------加锁后---------------------------"); log.debug(ClassLayout.parseInstance(list.get(i)).toPrintable()); } log.debug(ClassLayout.parseInstance(new MyInteger()).toPrintable()); // 新建对象,查看状态 }, "t3"); t3.start(); } }
3. 撤销数在 [20, 40) 内,超过 25s,撤销数已重置;次数不在该范围,不会重置
if(i == 38){ // 使其 25s 内完成不了 try { Thread.sleep(26000); } catch (InterruptedException e) { e.printStackTrace(); } }
锁粗化 && 锁消除
锁粗化:加锁、解锁会耗损性能;对锁不要过度细化,有时需要将锁范围扩大
锁消除:指的是虚拟机既时编辑器在运行时候,对一些代码上要求同步,但是对被检测到不可能存在共享数据竞争的锁进行一个消除(依据逃逸分析)。
本文深入探讨了Java中的偏向锁机制,包括其用途、偏向状态、延迟启动、加锁过程、锁撤销的原因及场景,如调用对象的hashCode和多线程使用同一对象锁。同时,详细阐述了批量重偏向和批量撤销的条件及影响,并通过实例展示了锁粗化和消除的概念,以提升并发性能。




















170万+

被折叠的 条评论
为什么被折叠?



