美文网首页
多线程安全中的Volatile和Synchronized关键字

多线程安全中的Volatile和Synchronized关键字

作者: 暴走的Jacky | 来源:发表于2018-05-30 17:02 被阅读0次

    内存模型

    内存模型.png

    在Java内存模型中,线程工作在自己的工作内存,他会保留主存的变量拷贝。对于普通变量,为了保证执行效率,在工作内存中对变量的改变并不会立刻刷新到主存中中。

    Volatile关键字

    volatile的意思是易变的,不需要被优化的。当一个变量被加上了这个关键字,就表示这个变量不需要被优化、缓存。对该变量的修改会被立刻刷新到主存中,而当其他线程读取该变量时,也会去主存中读取新值。

    使用场景

    1.修饰线程中断标志位

    volatile boolean isCancel;
    

    当我们希望通过标志位去结束一个正在运行的线程,那么应该对该标志修饰volatile。

    2.单例模式

    public class Singleton {
    private volatile static Singleton sInstance;    //保证对象的可见性
    private Singleton() {
    }
    public static Singleton getsInstance() {
        if (sInstance == null) {     //减少每次创建对象双重锁的开销
            synchronized (Singleton.class) {
                if (sInstance == null) {
                    sInstance = new Singleton();
                }
            }
        }
        return sInstance;
    }
    }
    

    问:为什么这里的volatile关键字是必要的?
    答:因为创建对象不是一个原子操作。

    禁止指令重排
    假设创建一个对象需要3个步骤:
    (1)分配内存空间。
    (2)初始化对象。
    (3)将内存空间的地址赋值给对应的引用。
    但是由于操作系统可以对指令进行重排序,所以上面的过程也可能会变成如下过程:
    (1)分配内存空间。
    (2)将内存空间的地址赋值给对应的引用。
    (3)初始化对象

    如果采用后面的流程,可能导致引用不为null,但是对象还没有初始化出来,其他的线程进行非空判断后,会引用这个对象,导致错误。

    volatile的原理和机制
    下面这段话摘自《深入理解Java虚拟机》:

    “观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”
    

    lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

    1.它确定指令重新排序时不会把其后的指令排在屏障之前,也不会把前面的指令排到屏障之后。即执行到屏障指令时,其前面的指令已经全部执行完,且该屏障指令的执行结果对后面的指令可见。
    2.它会强制对缓存的修改操作立刻写入主存
    3.如果是读取操作,它会导致其他线程中的缓存变量无效

    synchronized关键字

    要理解这个关键字的作用,就必须明白Java线程安全中的一个重要概念---原子性
    在Java中,对基本数据类型变量的读取和赋值操作是原子性的。
    如果要实现更大范围的原子性操作,就必须用synchronized和Lock来实现,他们能够保证任一时刻只有一个线程执行该代码块,从而保证了原子性。

    当访问临界资源时,为了避免数据不一致,我们会采取同步操作,以确保在某一时刻只有一个线程在操作资源。

    synchronized的用法

    1. 对象锁
      修饰对象方法或者明确指定某一个对象

    2. 类锁
      修饰静态方法或者锁定.class

    同一把锁在某一时刻只能被一个线程持有,当另一个线程尝试着获取该锁的时候,会陷入阻塞状态。JVM维持了一个等待该锁的队列,一旦该锁被某个线程释放了,那么这个等待队列中的线程会重新进入Ready(可运行)状态,等待调度器的下一次分配,进而去获取需要的锁。

    无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是类,该类所有的对象用同一把锁。

    相关文章

      网友评论

          本文标题:多线程安全中的Volatile和Synchronized关键字

          本文链接:https://www.haomeiwen.com/subject/sndojftx.html