其他分享
首页 > 其他分享> > volatile和synchronized的底层实现原理

volatile和synchronized的底层实现原理

作者:互联网

一:volatile

关于volatile的功能无非是两个: 1:保证保证线程间变量的可见性 2:防止指令重排序 下面我们就从字节码层面,JVM层面,CPU层面来解析是如何做到的。

1:字节码层面

首先编写一个很简单的类:

public class TestVolatile {
    int i;
    volatile int j;
}

编译并查看字节码: 由此可知:在字节码层面,加上volatile会给变量加上ACC_VOLATILE访问标识

2:JVM层面

这部分,无法用代码验证了,理论分析,其实是加了JVM层面的内存屏障,注意,是JVM规范的内存屏障,并不是真正的内存屏障。 JVM规范了四种内存屏障

而volatile则是在内存区的读写,都加屏障,具体如下,建议背过: StoreStoreBarrier:volatile写操作未执行前,保证前面的写操作完成 volatile 写操作 StoreLoadBarrier:volatile写操作执行完成前,后续不进行读写操作 LoadLoadBarrier:volatile 读操作未执行前,保证前面的读操作完成 volatile 读操作 LoadStoreBarrier:volatile 读操作执行完成前,后续不进行写读操作

3:CPU层面层面

上面说了JVM层面的内存屏障,其实说到底,JVM只是在内存中运行的一个软件而已,真正保障volatile功能还是依靠底层的硬件。 其实,cpu也有内存屏障,但是只有三条。 写屏障 sfence: store| 在sfence指令前的写操作当必须在sfence指令后的写操作前完成。 读屏障 lfence:load | 在lfence指令前的读操作当必须在lfence指令后的读操作前完成。 读写屏障 mfence:modify/mix | 在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。 还有条汇编指令Lock 原子指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。 Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序 总结:其实,volatile是windows系统使用lock 指令实现 | MESI实现(MESI:缓存一致性协议,基于inter类型的CPU)

二:synchronized

1:字节码层面

同样的,synchronized的作用是实现同步操作我们主要来分析它的实现原理。 首先,创建一个类:

public class TestSync {

    //加在方法m上
    synchronized  void m(){}

    //加在方法n上的代码块上
    void n() {
        synchronized (this) {
        }
    }

    public static void main(String[] args) {

    }
}

查看字节码:

从字节码可以看出,在方法上和代码块上加synchronized在字节码层面的实现是不一样的: 加在方法上:多了一个ACC_SYNCHRONIZED的访问标志 加在代码块上:1个monitorenter 两个monitorexit —>告诉虚拟机需要加锁

注意:这里为什么要两个monitorexit?

答:众所周知,synchronized在执行完成后会释放锁,monitorenter是加锁的开始,第一个monitorexit是正常执行完成后跳出释放锁的,第二个monitorexit是执行过程中如果抛出异常,是synchhronized自动退出的,防止继续持有锁,防止死锁的产生。

2:JVM层面

无法用代码实现,但我们都知道,Java是基于C/C++实现的,所以是C /C++ 调用了操作系统提供的同步机制

3:CPU层面

也是调用了CPU的lock指令 ,X86 : lock cmpxchg / xxx

标签:volatile,JVM,编写,操作,创建,C++,指令
来源: