美文网首页
Volatile,你想知道的这里都有

Volatile,你想知道的这里都有

作者: TUCJVXCB | 来源:发表于2019-08-07 23:15 被阅读0次

    volatile关键字,是Java面试中避免不了的一个问题,我们来好好的剖析一下这个JUC的关键字。


    volatile 是Java提供的一个轻量级同步机制。它保证了内存的可见性,不保证原子性,禁止指令重排序。我们用实际代码来分析这三个方面,分析完之后你就知道了为什么volatile是一个轻量级的同步机制了。

    • 可见性
      由JMM内存模型可以知道,线程拥有自己的工作内存,该工作内存由自己独享,其他线程访问不了。线程在进行读写操作时,要从主内存中拷贝一份数据放入自己的工作内存中,然后在自己的工作内存中进行读写操作,操作完后,再放回主内存。可见性就是如果一个变量被更新完放入主内存后,他会通知其他的线程,然后其他线程就会从主内存中拷贝最新的数据进自己的工作内存。理论就这么多,用两个例子来证明可见性。
    import java.util.concurrent.TimeUnit;
    
    
    public class Test {
        public static void main(String[] args) {
            Data data = new Data();
    
            /*
                我们在这里新建一个线程,这个线程调用了Data类的方法,修改了number的值,但是主线程Main发现不了
             */
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + " come in " +data.number);
                try {
                    TimeUnit.SECONDS.sleep(3);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                data.change();
                System.out.println(Thread.currentThread().getName() + " 修改已经完成~ " + data.number);
            },"A").start();
    
            while (data.number == 0){
    
            }
            System.out.println("已检测到number值发生变化~");
        }
    }
    
    /*
        资源类
     */
    class Data {
        int number = 0;
        public void change() {
            number = 5;
        }
    }
    

    程序的运行结果可想而知,主线程一直在while中出不去,因为它没有更新number的值。


    image.png

    我们加上volatile后试试

    class Data {
        volatile int number = 0;
        public void change() {
            number = 5;
        }
    }
    
    image.png

    加上volatile之后,Main线程会被通知number的更新操作,所以看到这里相信你应该了解了volatile的保证可见性了。

    • 不保证原子性
      那么什么是原子性?原子性就是不可再分且不可简化,要么全部成功 ,要么全部失败。原子操作就是一旦这个操作开始之后,直到这个操作完成,都不能中断这个操作。
    public class Test {
        public static void main(String[] args) {
            Data data = new Data();
            /*
                创建20个线程,让每个线程调用1000次add方法,如果保证原子性的话,答案是20000
             */
            for (int i = 1; i <= 20; i++) {
                new Thread(()->{
                    for (int j = 1; j <= 1000; j++) {
                        data.add();
                    }
                }).start();
            }
            /*  阻塞Main线程直到每个线程都完成计算任务后
                因为程序中本身就有Main线程和GC线程,如果线程数大于2,证明还有线程没计算完
             */
            while (Thread.activeCount() > 2) {
                Thread.yield();
            }
            System.out.println(data.number);
        }
    }
    
    class Data {
        volatile int number = 0;
        
        public void add() {
            number++;
        }
    }
    

    程序运行结果并没有出乎意料,


    image.png
    image.png
    image.png

    由此可以证明volatile不保证原子性

    • 禁止指令重排序
      指令重排序是操作系统对程序编译的优化,在单线程程序下,指令重排序之后的结果不会改变,但是在多线程环境下,不能保证数据的一致性,volatile能禁止指令重排序

    相关文章

      网友评论

          本文标题:Volatile,你想知道的这里都有

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