Java中volatile关键字你真的理解了吗?

面:你怎样理解volatile关键字时?

我:不加思索的说出,volatile修饰的成员变量,可保证线程可见性不保证原子性禁止指令重排

面:你能谈谈什么是线程可见性吗?

我:各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作,操作完成后再写回主内存中的.例如一个线程AAA修改了共享变量X的值还未写回主内存中时 ,另外一个线程BBB又对

内存中的一个共享变量X进行操作,但此时A线程工作内存中共享变量X对线程B来说并不不可见。这种工作内存与主内存同步延迟现象就造成了可见性问题。

面:你能通过代码验证线程可见性吗?

我:  好的,写出以下代码,以为很完美,运行也正常,心里美滋滋

class Child{

    private volatile  boolean cry = false;

    public boolean isCry() {
        return cry;
    }

    public void setCry(boolean cry) {
        this.cry = cry;
    }
    
}
public class MainTest {

    public static void main(String[] args)  {
         Child child = new Child();

        new Thread(()->{
            try {
                System.out.println("开始循环");
                while (!child.isCry()) {
                       System.out.println("观察宝宝。。。。。");
                };
                System.out.println("结束循环");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();


        new Thread(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            child.setCry(true);
            System.out.println("宝宝醒了。。。。");
        }).start();

        System.out.println("===============");



    }
}

面:你把volatile关键字删除,然后在运行一下?

我:运行一下发现,也能正常结束,我靠为什么会这样呢?我心里开始慌了,然后支支吾吾的。。。。

面:你看过System.out.println();的源码吗?

我:我又慌了,我说没看过(其实真没看过),肯定感觉我平时不怎么思考,而且刚才还再问volatile关键字怎么跳到这了呢。。。。

面:因为println()方法里面加了synchronized,而你在循环里里面调用了该方法,所以你懂了吧

 public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }  

我:我说大致明白了,因为synchronized能保证线程安全,肯定也具有线程可见性。

面:你这样理解也行,但是在使用了synchronized上锁这个操作后线程会做以下操作:

  1.获得同步锁

  2.清空工作内存

  3.从主内存中拷贝对象副本到本地内存

  4.执行代码(打印语句或加加操作)

  5.刷新主内存数据

  6.释放同步锁

面:volatile不能保证原子性,那你再不加锁的情况怎样保证原子性

我:我会结合java.util.concurrent.atomic包下的类配合使用,因为该包下的类可以保证原子性,通过CAS(比较并交换,个人理解:轻量级自旋锁)方式

面:你了解指令重排的含义吗?

我:大概了解一点,比如n++;它其实会被编译成四条指令,

//伪代码
public void test(){
        n++;
} //反编译字节码 public void test(); Code: 0: aload_0 1: dup 2: getfield #2 // Field n:I 5: iconst_1 6: iadd 7: putfield #2 // Field n:I 10: return }

面:恩,那你有时间可以内存屏障的概念,这样才能更深入的理解。

我:好的

面:那你在工作中如何使用volatile?

我:DCL单例模式,其它场景主要使用volatile的线程可见性

public class DCLSingleton {

    private static volatile DCLSingleton instance;

    private DCLSingleton(){

    }

    public static DCLSingleton getInstance(){
        if(null == instance){
            synchronized (DCLSingleton.class){
                if(null == instance){
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}  
posted @ 2020-05-27 17:08  码农的进击  阅读(186)  评论(0编辑  收藏  举报