美文网首页Java卓越之路-JAVA
volatile实践,你了解多少?

volatile实践,你了解多少?

作者: 一头光怪陆离的羊 | 来源:发表于2019-04-29 00:40 被阅读0次

    说volatile之前先要了解主内存,工作内存
    对于多线程来说,注意三个特性:1.原子性,2.有序性,3.可见性,这里不讲述
    volatile规则:

    • 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
    • 行指令重排序。
    • 保证有序性,可见性,不保证原子性

    使用volatile关键字会强制将修改的值立即写入主存,如当线程2进行修改时,会导致线程1的工作内存中缓存变量无效,由于线程1的工作内存中缓存变量无效所以会再次去主内存中去取
    如此,我们可以写一段代码验证一下

    public static void main(String[] args) {
            new Thread(){
                Test test = new Test();
                public void run(){
                    test.test1();
                }
            }.start();
            new Thread(){
                public void run(){
                    test.test2();
                }
            }.start();
        }
        int a = 0;
        public  void test1(){
            String name = Thread.currentThread().getName();
            System.out.println(name + " start");
            a = a+1;
            System.out.println(name+" a1--"+ a);
        }
        public  void test2(){
            String name = Thread.currentThread().getName();
            System.out.println(name+" a1 -- " + a);
        }
    

    可以看到,我起了两个线程分别调用test1对a做自增,test2打印,结果是什么呢?

    Thread-0 start
    Thread-0 a1--1
    Thread-1 a1 -- 0
    
    Thread-0 start
    Thread-0 a1--1
    Thread-1 a1 -- 1
    

    两种情况都可能,也就是说如果test1计算后将值写入主内存后test2才执行,这时是第二种从主内存中得到的是更改后的

    如果test1计算后还没来得及由工作内存写入主内存test2就开始执行了,从主内存中获取的是更改前的值后,test1才将值更新到主内存中,这就是第一种,就有问题了

    而用volatile修饰变量a就可以解决这个问题,结果就是第二种

    一开始偶然间犯了一个小错误,从而变得更有意思些

    public static void main(String[] args) {
            new Thread(){
                public void run(){
                    new Test().test1();
                }
            }.start();
            new Thread(){
                public void run(){
                    new Test().test2();
                }
            }.start();
        }
        volatile int a = 0;
        public  void test1(){
            String name = Thread.currentThread().getName();
            System.out.println(name + " start");
            a = a+1;
            System.out.println(name+" a1--"+ a);
        }
        public  void test2(){
            String name = Thread.currentThread().getName();
            System.out.println(name+" a1 -- " + a);
        }
    

    看下这样的运行结果是什么呢?

    Thread-0 start
    Thread-0 a1--1
    Thread-1 a1 -- 0
    

    虽然有volatile修饰,但是Thread-1得到的还是0,这是为什么呢?后来找了找原因才看到,虽然起了两个线程,但是也是起了两个test实例,每个线程分别new了一个实例,那么获取的也是不同的内存,不同的副本,这个怎么改一下呢?

    要么改成最上面那样new一个实例进行调用
    要么就加上static 即:static volatile int a = 0 ,这样共享一个实例就可以了


    还有一个问题,volatile不保证原子性怎么办?,可以看下这个

        volatile int a = 0;
        public static void main(String[] args) {
            final Test test = new Test();
            new Thread(){
                public void run(){
                    test.test1();
                }
            }.start();
            new Thread(){
                public void run(){
                    test.test2();
                }
            }.start();
        }
        public  void test1(){
            String name = Thread.currentThread().getName();
            System.out.println(name + " start-" + a);
            int b = a;
            System.out.println(name + "  b--"+ b);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            a = b +1;
            System.out.println(name+" a1--"+ a);
        }
        public void test2(){
            String name = Thread.currentThread().getName();
            a = a+1;
            System.out.println(name+" a1 -- " + a);
        }
    

    这个运行是什么?

    Thread-0 start-0
    Thread-0  b--0
    Thread-1 a1 -- 1
    Thread-0 a1--1
    

    为什么是这个结果?自增了两次,那么Thread-0和Thread-1结果应该是1和2啊,因为他们同时从主内存中获取a为0,test1()已经声明变量b也指向a的值0,然后等待,这时test2()执行+1操作,写入主内存为1,test1()睡醒b+1也为1写入a的主内存,那么就绕过了volatile的特性

    解决办法:synchronized,对同一变量+1操作采取同步
    Lock lock = new ReentrantLock();两者具体怎么用就不说了

    基础知识还是得狠狠补一下的啊,要想质的飞跃,就得脚踏实地

    相关文章

      网友评论

        本文标题:volatile实践,你了解多少?

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