ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

乐观锁和悲观锁

2021-05-22 19:31:55  阅读:216  来源: 互联网

标签:悲观 对象 乐观 CAS 线程 JVM new String


乐观锁和悲观锁:

CAS:Compare And Swap 比较和替换 是乐观锁的一种

是一种轻量级锁,在java的JUC中很多工具类的时效件都基于CAS的;

在这里插入图片描述

CAS保证线程安全的方法:

线程在读取数据时候不进行加锁,在准备写会数据的时候先去查询原值,操作的时候比较原始是否被修改,若未被其他先修改则写回,若已被修改,则重新执行读取流程。

在这里插入图片描述

存在的问题:ABA问题:

在这里插入图片描述

  1. 线程1进行读数据A,但是操作时间长没写回数据;
  2. 线程2进行读数据A;
  3. 然后线程2进行CAS对比原来还是A,可以修改为B;
  4. 线程3进行读数据B;
  5. 然后线程3进行CAS对比原来还是B,可以修改为A;
  6. 然后线程1结束操作准备写回数据,进行CAS比较原来还是A,写自己的数据。

其实数据是已经进行变化的,而A线程不知道而已;

解决:例如搞个自增的字段,操作一次就自增加一,或者搞个时间戳,比较时间戳的值。

而且CAS一直不成功会,相当于死循环,产生自旋,导致CPU压力变大。

unsafe

在这里插入图片描述

底层硬件支持 :最终执行时 lock cmpxchg指令 lock原子性,cmpxchg非原子性

悲观锁:JVM层的synchronize

加锁过程就相当于对临界资源的操作,PV操作,内部实施的就是一个monitor,这个留到后面来说

内存在内存中的布局:

在JVM中,对象在内存中分为四块区域,对象头(MarkWord)、类型指针(Class Pointer)、实例数据(Instance Data)、对其填充(Padding)。

三种就是对象头包含MarkWord和Class Pointer

在这里插入图片描述

public class ObjecyStructure {
    public static void main(String[] args) {
        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

前提都是压缩指针效果的:

在这里插入图片描述

public class ObjecyStructure {
    public static void main(String[] args) {
        Object o = new Object();
        synchronized (o){
            int i = o.hashCode();
            System.out.println(i);
        }
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
}

在这里插入图片描述

  • Mark Word:默认存储对象的HashCode,分代年龄和锁标志位信息。它会根据对象的状态复用自己的存储空间,也就是说在运行期间Mark Word里存储的数据会随着锁标志位的变化而变化。
  • Klass Point:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例

那么时间synchronize是怎样进行所操作的呢?

  • synchronized 应用在方法上时,在字节码中是通过方法的 ACC_SYNCHRONIZED 标志来实现的。
  • synchronized 应用在同步块上时,在字节码中是通过 monitorenter 和 monitorexit 实现的。
    • 每个对象对会有一个monitor相关联,当拥有了之后就会被锁住,并且有一个进行记录拥有次数的计数器,从0开始,有一个就加1一次。
    • 当对象进行释放锁的时候,执行的是monitorexit指令,然后计数器在进行减1,当计数器为0 时,其他线程就可以进行获取。

Object o = new Object()在内存中占多少字节?

压缩的 Markword8+class pointer4+对象4+补齐4=20 或者对象空0 16字节

没压缩的:8+8+0+0=16

一个User user = new User(Integer id,String name)在内存中占多少字节?

MarkWord 8个、Class Pointer 4个,Instance(4+4个)、Padding 4个 24个字节

锁升级:

在这里插入图片描述

并且锁不能降级,只能升级。

降级是JVM可以操作,没有实际意义,就是进行回收时。

在这里插入图片描述

锁升级条件:

  • new----->偏向锁:指针之向就行,相当于上一个标签说明,直接对标志为加1就行;偏向锁关闭,或者多个线程竞争偏向锁怎么办呢?

  • 偏向锁------>轻量级锁:跟MarkWord相关,JVM会在对应的线程的栈帧建立一个锁记录(Lock Record)的空间,用来存储锁对象的Mark Word 拷贝,使用LR的owner去指向当前对象。(产生了竞争关系)

在这里插入图片描述

  • 轻量级锁----->自旋锁----->重量级锁:自旋,过来的现在就不断自旋,防止线程被挂起,一旦可以获取资源,就直接尝试成功,直到超出阈值,自旋锁的默认大小是10次或者占一半内存资源升级为重量级锁,等待唤起

    -XX:PreBlockSpin可以修改。

在这里插入图片描述

锁消除

pub1ic void add(String strl,String str2){
StringBuffer sb = new stringBuffer();
sb. append(str1). append(str2);

我们都知道StringBuffer是线程安全的,因为它的关键方法都是被synchronized修饰过的,但我们看上面这段代
码,我们会发现,sb这个引用只会在add方法中使用,不可能被其它线程引用(因为是局部变量,栈私有),因
此sb是不可能共享的资源,JVM 会自动消除StringBuffer对象内部的锁。

锁粗化

public String test(String str){
I
inti=0;
stringBuffer sb = new stringBuffer():
while(i < 100){
sb. append(str);
i++;
}
return sb. tostring():
}

JVM会检测到这样-连串的操作都对同一 个对象加锁(while 循环内100次执行append,没有锁粗化就是执行100次加锁/解锁),此时JVM就会将加锁的范围粗化到这一-连串的操作的外部(比如while虚幻体外),使得这一连串操作只需要加一-次锁即可。

标签:悲观,对象,乐观,CAS,线程,JVM,new,String
来源: https://blog.csdn.net/weixin_44641846/article/details/117170238

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有