java多线程(三) volatile关键字

2019-08-27  本文已影响0人  只会写一点点代码

一、用图说话

在这里插入图片描述

二、情景在线

三、多线程的三大特性。

    public  static int i=20;

    public static void main(String[] args) {


        Thread t1=new Thread(()->{
            i++;
        });
        t1.start();
        Thread t2=new Thread(()->{
            i++;
            System.out.println(Thread.currentThread().getId()+":"+i);
        });
        t2.start();
        System.out.println("---------"+i);
    }
}

多执行几次会发现输出的结果和预想的不一样。但是对i加上了volatile关键字后,在看看结果。
   int i=0;
   boolean flag=false;
   i=10;    //语句1
   flag=true; //语句2
   //对于语句1和语句2执行的顺序会1->2吗?其实不一定,对于语句1和语句2谁先之执行对程序其实没
   多大影响。
   ** 但是注意的是:cpu不是🐷,不会瞎搞指令重排,对于有关联的操作,比如操作A,需要操作B的结果,
   那么操作A就不会在操作B的前面。
很容易理解,对于单线程来说,其实指令的重排并没有什么影响。
package com.pjw.Thread.vo;

public class Reorder {



    static  boolean flag=false;
    public static void init(){
        System.out.println("初始化操作完成了");
    }

    public static void main(String[] args) {



        Thread t1=new Thread(()->{
            init(); //1⃣️
            flag=true; //2⃣️
        });




        Thread t2=new Thread(()->{
            while (flag){
                try {
                    System.out.println("进入了方法");
                    Thread.sleep(50000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            System.out.println("没进入while循环");

        });

        t1.start();
        t2.start();

     //运行以上的代码,在多次测试后会发生两种情况,对于1,2两句指令,没有特殊的关联,所以谁先运行
     你我也不知道,只有cpu知道,那么对于正常的流程,语句1先运行进行初始化,然后对flag进行赋值,是
     正确的,但是如果语句2先运行,那么结果是不是就错误了呢?
     我们加上volatile关键字以后在看一看效果。

   //所以得出结论,有序性对于多线程程序也是需要的,没法保证有序性会造成程序的错误。
    }
}
介绍一下:
   Two actions can be ordered by a happens-before relationship. 
   If one actionhappens-before another, then the first is visible to and ordered before the second.
   上述为概论:简单的翻译一下,就是两个动作如果符合happens-before原则,第一动作必先发生在第二个
   动作以前,并且对其可见。
   1、If x and y are actions of the same thread and x comes before y in program order,then hb(x, y);
     (对于同一个线程中两个动作,写在前面的必先发生在后面的以前。)
   2、If hb(x, y) and hb(y, z), then hb(x, z)
       (传递规则,如果x先于y,y先于z,则x先于z)
   3、An unlock on a monitor happens-before every subsequent lock on that monitor
   (一个锁释放操作先于后面对同一把锁的获得操作)
   4、A write to a volatile field (§8.3.1.4) happens-before every subsequent read ofthat field.
   (一个volatile的写操作先于读操作。)
   5、A call to start() on a thread happens-before any actions in the started thread.
   (一个线程的start方法,先于这个线程的任何方法)
   6、All actions in a thread happen-before any other thread successfully returns froma join() on that thread.
   (线程中的所有操作先于这个线程的中止检测,比如说join()方法或islive()方法)
   7、There is a happens-before edge from the end of a constructor of an object to thestart of a finalizer
          for that object.
         (线程的初始化操作先于他的回收操作,想想如果我还没初始化就被干掉,我得郁闷死啊)

解释一下第一条:我们刚才的代码不就是同一个线程的中两个动作吗?还是指令重排了啊,因为第一个只保证
单线程的情况下,对于多线程是不保证的。

四、作用

package com.pjw.Thread.vo;

public class AtomicDemo {

    public volatile int inc=0;

    public void increase(){
        inc++;
    }

    public static void main(String[] args) {
        final  AtomicDemo demo=new AtomicDemo();

        for(int i=0;i<10;i++) {

            new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        demo.increase();
                    }
                };
            }.start();
        }


        System.out.println(demo.inc);
//实验发现,每次输出的结果都不到1000,是因为volatile只保证了可见性,没有保证原子性。
inc++不是原子操作,如果线程一只是load完inc以后,并没有++操作,就被等待,其实对于其他线程
拿到的数据还是线程一的原始数据,没有达到我们需要的效果。其实这样可以使用atomic包或锁,保证原子操作下一节介绍CAS等操作。
    }
}

五、解析以前单例模式的困惑。

六、总结

细细研究还是很多东西的,路漫漫其修远兮,吾将上下而求索。

上一篇 下一篇

猜你喜欢

热点阅读