批量
synchronized中的偏向锁是将线程id标记到markword中以占用锁
1.撤销
当对象调用hashcode时,会把hashcode存到markdown,这个hashcode是延迟更新的,只有调用的时候才会填入,所以这个时候如果有偏向锁,hashcode会将偏向锁挤走,也就是末尾bit置为001正常状态。
当线程1被标记了偏向后,线程2也调用了锁,两个线程之间不存在竞争关系,则会发生偏向撤销,也就是锁会升级成轻量级锁,状态从101->000->001.
2.批量重偏向。
当频繁出现撤销时(初始阈值为20),jvm会认为该类的锁是否确实标量了线程1.从而会运行其重偏量到其他线程,状态也从001->101,即线程2的偏向状态。
实现步骤:
修改线程2的锁列表范围0-40,前0-19现象为撤销101->000->001,后20-40的现象为重偏向101->101->101
之后所有从线程1修改太到其他线程的偏向锁都会重新偏向。
3.批量撤销。
当出现频繁撤销时(阈值为40),jvm会认为该类确实不应该偏向线程1,则会将该类的所有对象全部不可偏向,包括新生成的锁对象。
实验步骤:
修改线程2的锁列表范围0-40,其中0-19现象为撤销101->000->001,后20-40的现象为重偏向101->101->101,线程3的锁列表范围为20-50,其中20-40的锁偏向为从线程2的修改偏向,会计为偏向撤销101->000->001,升级到轻量级锁,40-50则会触发批量撤销条件自动取消偏向状态也为撤销101->000->001。
所以,40触发条件的撤销为从线程1转为线程2的20个撤销+线程2转为线程3的20个撤销。
代碼:
package com.lizhenxin.java0422.b3; import com.lizhenxin.java0422.util.Sleeper; import lombok.extern.slf4j.Slf4j; import org.openjdk.jol.info.ClassLayout; import java.util.ArrayList; import java.util.List; import java.util.Vector; /** * @author lizhenxin * @create 2022-04-24-18:05 */ @Slf4j public class Test4 { public static void main(String[] args) throws Exception { // Sleeper.sleep(1); int forCount = 100; Vector<Dog> list = new Vector<Dog>(); Thread t1 = new Thread(() -> { for (int i = 0; i < forCount; i++) { Dog d = new Dog(); list.add(d); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } } synchronized (list) { list.notify(); } }, "t1"); t1.start(); Thread t2 = new Thread(() -> { synchronized (list) { try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("===============> "); for (int i = 0; i < 40; i++) { Dog d = list.get(i); log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } synchronized (MyClassLayout.class) { MyClassLayout.class.notify(); } }, "t2"); t2.start(); Thread t3 = new Thread(() -> { synchronized (MyClassLayout.class) { try { MyClassLayout.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("===============> "); for (int i = 20; i < 50; i++) { Dog d = list.get(i); log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } synchronized (lock.class) { lock.class.notify(); } }, "t3"); t3.start(); Thread t4 = new Thread(() -> { synchronized (lock.class) { try { lock.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("===============> "); for (int i = 20; i < forCount; i++) { Dog d = list.get(i); log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); synchronized (d) { log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } log.debug(i + "\t" + MyClassLayout.printMarkDown(d)); } }, "t4"); t4.start(); t4.join(); log.debug(MyClassLayout.printMarkDown(new Dog())); } } class Dog{ } class MyClassLayout{ static String printMarkDown(Object instance){ String[] split = ClassLayout.parseInstance(instance).toPrintable().split("\\r\\n"); String s = split[2]; return s.substring(60,s.length()); } } class lock{ }
maven:
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.9</version> </dependency>
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现