您的位置:首页 > 娱乐 > 明星 > 成都学校网站制作公司_公共交易中心招标网_信息流广告怎么投放_白百度一下你就知道

成都学校网站制作公司_公共交易中心招标网_信息流广告怎么投放_白百度一下你就知道

2024/12/23 3:46:42 来源:https://blog.csdn.net/2301_79748665/article/details/144410790  浏览:    关键词:成都学校网站制作公司_公共交易中心招标网_信息流广告怎么投放_白百度一下你就知道
成都学校网站制作公司_公共交易中心招标网_信息流广告怎么投放_白百度一下你就知道

目录

  • java对象头
    • 一:Monitor
    • 二:sychronized的优化
      • 轻量级锁
      • (轻量级锁)锁膨胀(重量级锁)
      • (重量级锁)锁自旋
      • 偏向锁(比轻量级锁更轻量)
        • 偏向锁状态
        • 如何撤销偏向锁
        • 批量重偏向
        • 批量撤销
      • 锁擦除

java对象头

一:Monitor

  • Monitor翻译过来可以叫监视器和管程。
  • 当给某个对象加上锁之后,这个对象的对象头中的MarkWord就会指向一个Monitor。

结构如下

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 刚开始OWner为空,当有线程获得锁时,owner就会指向该线程;

  • 当有其他线程执行到sychronized时,因为一个owner只能指向一个线程,所以它就会进入EntryList进入BLOCKED状态

  • 当线程执行完释放锁时,会通知Monitor,Monitor会唤醒EntryList中的线程,然后这些线程会进行非公平竞争来获取锁;

    注意:

  • 一个对象只有一个Monitor

二:sychronized的优化

  • 故事

  • 故事角色

    • 老王 - JVM
    • 小南 - 线程
    • 小女 - 线程
    • 房间 - 对象
    • 房间门上 - 防盗锁 - Monitor
    • 房间门上 - 小南书包 - 轻量级锁
    • 房间门上 - 刻上小南大名 - 偏向锁
    • 批量重刻名 - 一个类的偏向锁撤销到达 20 阈值
    • 不能刻名字 - 批量撤销该类对象的偏向锁,设置该类不可偏向

    小南要使用房间保证计算不被其它人干扰(原子性),最初,他用的是防盗锁,当上下文切换时,锁住门。这样即使他离开了,别人也进不了门,他的工作就是安全的。

    但是,很多情况下没人跟他来竞争房间的使用权。小女是要用房间,但使用的时间上是错开的,小南白天用,小女晚上用。每次上锁太麻烦了,有没有更简单的办法呢?

    小南和小女商量了一下,约定不锁门了,而是谁用房间,谁把自己的书包挂在门口,但他们的书包样式都一样,因此每次进门前得翻翻书包,看课本是谁的,如果是自己的,那么就可以进门,这样省的上锁解锁了。万一书包不是自己的,那么就在门外等,并通知对方下次用锁门的方式。

    后来,小女回老家了,很长一段时间都不会用这个房间。小南每次还是挂书包,翻书包,虽然比锁门省事了,但仍然觉得麻烦。

    于是,小南干脆在门上刻上了自己的名字:【小南专属房间,其它人勿用】,下次来用房间时,只要名字还在,那么说明没人打扰,还是可以安全地使用房间。如果这期间有其它人要用这个房间,那么由使用者将小南刻的名字擦掉,升级为挂书包的方式。

    同学们都放假回老家了,小南就膨胀了,在 20 个房间刻上了自己的名字,想进哪个进哪个。后来他自己放假回老家了,这时小女回来了(她也要用这些房间),结果就是得一个个地擦掉小南刻的名字,升级为挂书包的方式。老王觉得这成本有点高,提出了一种批量重刻名的方法,他让小女不用挂书包了,可以直接在门上刻上自己的名字

    后来,刻名的现象越来越频繁,老王受不了了:算了,这些房间都不能刻名了,只能挂书包

轻量级锁

  • 当一个对象有多线程要加锁,但是多线程加锁的时间是错开的,这个时候我们可以使用轻量级锁来优化
  • 轻量级锁是透明的,即我们还是使用sychronized方法进行加锁;

我们研究一下一段代码:

static final Object obj = new Object();public static void method1() {synchronized( obj ) {// 同步块 Amethod2();}
}public static void method2() {synchronized( obj ) {// 同步块 B}
}
  • 这里加锁的流程:
  • 刚开始加锁会在线程的栈帧中加入一个锁记录对象,然后将锁记录中的对象引用指向加锁对象的地址;

  • 然后尝试用cas替换对象头中的MarkWord,将MarkWord存入锁记录;

  • 如果cas成功,对象头中就保存了锁地址和状态00;表示该线程给对象加锁
  • cas失败有两种情况:
  • 1:如果其他线程已经持有了Object轻量级锁,说明具有锁竞争,进入锁膨胀状态;
  • 2:如果是synchronizad锁重入,那么就会在加一条lock record记录重入的次数,这里的锁记录为null;
  • 然后继续指向,再次对这个对象加锁,就会形成synchronized锁重入现象,创建一个lock recode记录重入次数:

  • 当解锁时,如果lock record的锁记录为空那么就删除该锁记录,同时重入次数-1;
  • 如果解锁是,lockrecord的锁记录不为空,那么就会使用cas将MarkWord恢复给对象
    • 成功
    • 失败:说明进行了所膨胀或者是升级为重量级锁,进入重量级锁解锁流程;

(轻量级锁)锁膨胀(重量级锁)

  • 在cas失败,也就是已经有线程持有object锁对象了,那么就会进入锁膨胀,将轻量级锁转变为重量级锁。
  • 首先Object会申请Monitor,然后object会指向Monitor的地址,然后将线程加入到EntryList进入BLOCKED状态进行等待;
  • 然后原本持有轻量级锁的线程使用cas将MarkWord还给对象时会失败,然后就会根据Monitor的地址找到Monitor,将Owner置为空,然后唤醒EntryListBLOCKED的线程;

(重量级锁)锁自旋

在重量级锁竞争过程中,会通过自旋(循环获取重量级锁)来进行优化,如果获取锁成功(持锁线程释放了锁),就可以避免进入阻塞状态(从阻塞再恢复会进行上下文切换,比较耗费性能)

注意:

  • 自旋回占用cpu资源,单核进行自旋没有意义,多核才有效果
  • java6 之后的锁自旋是很智能的;
  • java7之后可以开启或者关闭锁自旋;

偏向锁(比轻量级锁更轻量)

  • 我们在使用轻量级锁在没有竞争时进行锁重入的时候,还是会进行cas操作;
  • 我们可以使用偏向锁,偏向锁只会在第一次获取锁时使用cas将线程的ID设置为对象头中的MarkWord,之后在没有竞争的情况下,发现线程id是线程本身的话就不会进行cas操作,对象属于该线程;
偏向锁状态

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 一个对象在创建时:

    • 默认是可以使用偏向锁,他的MarkWord后三位默认是101,其他位都是0;
    • 但是偏向锁开启默认都是延时的,想要避免延时可以加上Vm参数:-XX:BiasedLockingStartupDelay=0禁用延迟
    • 如果禁用了偏向锁,那么创建对象之后的MarkWord后三位是001;

    我们可以来测试偏向锁:


public static void main(String[] args) throws IOException {Dog d = new Dog();ClassLayout classLayout = ClassLayout.parseInstance(d);new Thread(() -> {log.debug("synchronized 前");System.out.println(classLayout.toPrintableSimple(true));synchronized (d) {log.debug("synchronized 中");System.out.println(classLayout.toPrintableSimple(true));}log.debug("synchronized 后");System.out.println(classLayout.toPrintableSimple(true));}, "t1").start();
}

输出:

11:08:58.117 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 
11:08:58.121 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 
11:08:58.121 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101

这里关闭了延时所以刚开始就是101,然后加上偏向锁之后在Markword中就多了线程ID,之后就是释放了锁也是有线程ID,这个就是偏向,加锁之后就属于该线程了;

如果禁用偏向锁:

11:13:10.018 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 
11:13:10.021 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00100000 00010100 11110011 10001000 
11:13:10.021 c.TestBiased [t1] - synchronized00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001

加锁之后默认使用的是轻量级锁,是00;

在调用hashcode之后会车撤销偏向锁,因为对象头中没有位置;

我们知道锁的优先级是偏向锁,轻量级锁,重量级锁;

如何撤销偏向锁
  • 使用hashcode
  • 其他线程来获取锁(不是同时来竞争。出现竞争就会转变成重量级锁了)
  • 使用wait/notify
批量重偏向
  • 当对象被多线程访问,但是没有竞争时,此时偏向于t1线程的偏向锁有机会偏向t2线程,会重置线程ID;
  • 当撤销偏向锁的阈值超过20次时,jvm会认为偏向锁偏向错了,会在加锁时重新偏向于新线程;
批量撤销
  • 当撤销锁的阈值超过40次时,Jvm就会判断,是不是不应该偏向,于是这个类的所有对象都变成了不可偏向,新建的对象也都是不可偏向;

锁擦除


@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@Warmup(iterations=3)
@Measurement(iterations=5)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {static int x = 0;@Benchmarkpublic void a() throws Exception {x++;}@Benchmarkpublic void b() throws Exception {//这里的o是局部变量,不会被共享,JIT做热点代码优化时会做锁消除Object o = new Object();synchronized (o) {x++;}}
}
  • 这里要判断加锁和不加锁的性能差距,最后得出的结果是差不多的,为什么呢
  • 因为JIT即时编译器会对热点代码进行优化,这里他就会判断o变量是局部变量,没有逃出方法的作用范围不会被共享。是线程安全的所以会对其进行优化擦去锁;

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com