Volatile与synchronized详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/dadajixxx/article/details/92762514

文章目录:

1.简单理解Volatile和synchronized

2.Volatile详解

3.synchronized详解

4.Volatile与synchronized的区别与联系


1.简单理解Volatile和synchronized:

synchronized:当被synchronized修饰时,synchronized会锁定当前变量,只有当前线程能够访问该变量,其他线程会被阻塞。

volatile:当volatile修饰变量时,编译器对此变量的读写操作不做任何优化,每次都会去内存去读,写也是写到内存,不做任何的缓存优化

volatile,final,synchronized都可以实现可见性。

2.Volatile详解

volatile可见性原理

可见性:任何线程能马上看到它的结果

volatile的本质是告诉jvm这个变量是在寄存器中的值是不确定的,加入volatile关键字以后,保存数据会同步到主内存中,而另一端读数据时也会从主内存中读取,volatile使用cpu的缓存锁来保证可见性。

volatile

 

volatile禁止指令重排序

指令重排序的目的: 提高cpu运算效率

cpu提高利用率的过程:进程 -> 线程 -> 指令 

指令重排序到底是什么?

举个栗子:写代码每一行都是一个指令,有的指令运行时间比较长,不能让其他指令排队等着,这时对指令进行重新排序,来提高运行效率

重排序

不管怎么重排序,单线程都遵循 as-if-serial语义,而多线程遵循 happen-before

讲清楚指令重排序了,volatile如何防止指令重排?

Volatile变量在赋值后会有一个lock add命令,这个命令相当于内存屏障,重排序时不能把屏障后的指令重排序到屏障之前。

volatile使用案例

案例1

在下面这个单例模式下,会指令重排序,如果这时多线程访问,这时还没有初始化对象,会发生不完整实例。 如图2-1

使用volatile修饰 instance 防止这种问题发生

public class VolatileDemo {

    private  volatile static  VolatileDemo instance;

    private VolatileDemo(){}
   
    /*
     * 懒汉模式 double check
     * 不完整实例,指令重排序导致多线程访问发生不完整实例,
     * 解决办法:    private volatile static  VolatileDemo instance;
     * 防止指令重排序
     * */

    public static VolatileDemo getInstance2(){
        if (instance == null ){
            synchronized (VolatileDemo.class){
                if (instance == null){
                    instance = new VolatileDemo();
                }
            }
        }
        return instance;
    }
}

 

图 2-1

案例2

volatile int i;
public void add{
    i++;
}

这里先要读i值 ,然后++,又同步。 再多线程操作时会出错,原理同案例1,有关指令重排序的问题导致的。

3.synchronized详解

当被synchronized关键字修饰的代码块在被编译成字节码的时候,会在该代码块开始和结束的地方加入 monitorenter 和 moniterexist 指令,任何对象都有一个monitor相关联,当一个monitor被持有后,他就处于锁定状态,当线程执行到monitorenter指令时,会获尝试获取对象对应的monitor的所有权,即获取对象的锁。monitor指令如图3-1

图 3-1

虚拟机在执行这两个命令的时候,会检查对象的锁状态是否为空或当前线程是否已经拥有对象的锁,

如果是 则对象锁的计数器加 1 ,直接进入同步代码,

如果不是,则当前线程阻塞等待,等待锁释放。

4.Volatile与synchronized的区别与联系

(1)volatile,final,synchronized都可以实现可见性

(2)volatile的本质是告诉jvm这个变量是在寄存器中的值是不确定的,需要从主存中读取。

(3)synchronized是锁定当前变量,只有当前线程能够访问该变量,其他线程被阻塞。

(4)volatile仅能实现变量的修改可见性,不具备原子性,而synchronized可以实现变量的修改可见性和原子性

(5)volatile标记的变量不会被编译器优化,而synchronized可以(禁止指令重排序)

(6)volatile使用在变量级别,而synchronized可以使用在变量和方法

(7)volatile不会阻塞线程,而synchronized可能会

 

如果还想看更多关于并发编程知识,这里有一份我的总结

https://blog.csdn.net/dadajixxx/article/details/88427190

猜你喜欢

转载自blog.csdn.net/dadajixxx/article/details/92762514