CAS、synchronized和volatile

一.CAS

CAS

native方法。修改一个值,当前为0,现在要加一,在写回的时候,判断该变量是否还是0,。

ABA问题

CAS会有一个问题,如果该变量还是0,不一定代表他没有被人修改过。比如另一个线程对他加2,然后又被减2,虽然最后还是0,但是他不是最开始的那个0.
解决办法:

  • 可以加一个bool表示是否修改过
  • 加一个版本号

CAS底层汇编实现

AtomicInteger 一步一步查到最后。
java native代码->虚拟机jvm的c++代码->linux的汇编代码

lock cmpxchg

lock的意思是后面的指令不能被其他CPU打断,这样就能保证在cmp的时候值是不变的。

jdk1.8 Unsafe类

Unsafe类里面有很多CAS方法,以AtomicInteger用到的getAndAddInt为例。

public final int getAndAddInt(Object var1, long var2, int var4) {
    // 三个参数分别是内存的值,期望值,也就是当前读到的值,修改之后的值
    //只有前两个值相等,才会把第三个值更新到内存
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

注意单纯的compareAndSwapInt不会循环,只会compare一次,并返回bool值。

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

二.Synchronized

对象内存布局

对象内存=markword(锁信息 8字节)+class pointer(类型指针,表示属于哪个类 4字节)+instance data(实例数据)+padding(对齐 需要为8的整数倍)。所以new一个什么都没有的Object,new Object()是16个字节(8(markword) + 4(class pointer) + 4(padding))。
注意64位虚拟机开启压缩之后class pointer是4字节,不压缩是8字节

示例

以user{int id,string name}为例
markword 8
classpointer 4(压缩)
int 4
string 4(压缩)
padding 4
所以一共是24字节

markword+class pointer是对象头。

synchronized加锁过程

首先synchronized是锁住对象,不是锁住代码块。
synchronized的锁自动升级过程:

new Obj(无锁)->偏向锁->轻量级锁(自旋锁,自适应自旋)->重量级锁

在这里插入图片描述

锁降级的过程:
来源:知乎
重量级锁的降级发生于STW阶段,降级对象就是那些仅仅能被VMThread访问而没有其他JavaThread访问的对象。也就是说只有GC的时候才降级,那对象都没了,降级不降级也没有意义了。
汇编语言实现方式:lock cmpxchg

锁消除

    public String test(String a,String b) {
        StringBuffer sb = new StringBuffer();
        sb.append(a).append(b);
        return sb.toString()}

上面这段代码在实际运行时,JVM会检测出加锁对象都在一个方法里面,所以为了避免反复加锁,JVM不会加上锁。

锁粗化

    public String test(String a,String b) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 100; i++) {
            sb.append(i);
        }
        return sb.toString();
    }

JVM检测到这样的代码,循环的反复加锁解锁,JVM会把加锁操作放到循环体外,这样只用加一次锁。

三.volatile

volatile有两个作用:

  • 保证线程可见性(内存屏障,工作内存强制写入内存)
  • 防止指令重排序

单例模式中有这段代码:

if (singleton != null) {
    synchronized(Singleton.class) {
        if (singleton != null) {
            singleton = new Singleton();
        }
    }
}

对singleton加上volatile是防止第二次检查时候,new指令会发生重排序。
new在编译器编译时会把这个语句分成三步

  • 1、分配空间
  • 2、赋值
  • 3、初始化

编译器编译之后的指令就不再是123,而是132 。此时如果线程1执行完13之后把时间片交给线程2,线程2取这个对象时发现已经有地址了,不是null,就会取走,但是这个对象并没有赋值,之后线程2就会出错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值