谈谈volatile

volatile的作用:

  volatile关键字的作用包括:保障可见性,保障有序性。

何为保障可见性,看下面的代码:

package com.mashibing.thread.lock;

public class TestVolatile {
    public static void main(String[] args) {
        try {
            Thread8 t = new Thread8();
            t.start();
            Thread.sleep(1000);
            t.setRunning(false);
            System.out.println("已赋值false");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class Thread8 extends Thread {
    private boolean isRunning = true;

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean isRunning) {
        this.isRunning = isRunning;
    }

    public void run() {
        System.out.println("进入run()了");
        while (isRunning) {
        }
        System.out.println("线程被停止了");
    }
}
View Code

我们在主线程里面,启动一个线程,然后去修改的isRunning属性,达到中止线程中while循环的目的,但是实际我们运行代码的结果如下:

 

 

 很明显,线程没有停止。此时我们在isRunning变量加上vo;alite关键字,结果如下:

 

 

 这就达到了我们的目的了。所以volatile可以保障一个线程修改了共享变量,能够对其他线程保持可见。

何为保障有序性,看下面的代码:

package singleton;

/**
 * @ClassName SingletonDemo2
 * @Description 双重检查(比较完美的写法)
 * @Author liuyi
 * @Date 2020/6/7 13:23
 * @Version 1.0
 */
public class SingletonDemo4 {
    //必须加volatile关键字,防止指令重排
    private static volatile SingletonDemo4 instance;

    private SingletonDemo4(){

    }

    public synchronized static SingletonDemo4 getInstance(){
        //为什么要进行双重检查
        //比如两个线程同时进入该方法,都拿到instance为空,其中一个拿到锁并new了一个实例,
        //此时另外一个线程它并不知道你已经new了实例,所以当它拿到锁之后会继续new一个实例
        //所以如果在锁里面继续判断一次是很有必须要的
        if(instance==null){
            synchronized (SingletonDemo4.class){
                if(instance==null){
                    instance = new SingletonDemo4();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for (int i = 0; i <100 ; i++) {
            new Thread(()->{
                System.out.println(getInstance());
            }).start();
        }
    }
}
View Code

这里是一个经典的单例模式实现方式之一,我们用volatile关键字单例对象,这里为什么加volatile关键字,就是为了防止出现指令重排序,保障有序性。

我们来分析这里为什么需要防止出现指令重排序,

synchronized (SingletonDemo4.class){
                if(instance==null){
                    instance = new SingletonDemo4();
                }
            }
我们来看被锁住的这句代码,instance = new SingletonDemo4(),其实这句话在计算机底层是分为几个步骤实现的,那么就会一种情况,instance先被
写入到共享变量,然后再被初始化。因为,计算机底层的执行顺序是可以重新排列的,这种现象被称作为指令重排序。那么刚好这个时候,有其他的线程走到if(instance==null)
就会再次进行初始化,这就破环了单例。当然这种情况,只有在超高的并发下才可能出现,我们自己做测试很难出现。

 

  

posted @ 2020-08-13 22:50  负重前行的小牛  阅读(199)  评论(0编辑  收藏  举报