Bootstrap

volatile关键字作用

volatile是一个和多线程相关的关键字,主要有一下2点作用(只保证可见性,不保证原子性)

  1. 防止指令重排(有序性)
    JVM在不改变程序执行结果的前提下,在编译时会对指令的顺序进行重新排序,而volatile关键字则能够禁止指令的重新排序
  2. 能够确保线程内存中的对象对其他内存可见。(可见性)
    正常情况下每个线程操作共享变量时需要经历如下几个步骤
    在这里插入图片描述
    如果某个线程(线程01)要操作主内存中的变量A,则该线程会把A变量装载到线程内部的内存中做一个副本,之后线程操作的是线程内存变量A的副本,等到操作完成再将变量的值刷新到主内存中。假设变量A=0,此时有100个线程并发的对它进行+1,理想情况,最后A的值是100.但再设计情况中我们会发现,A的值并不会是100,(假设线程01读到的A的值是0,而线程02读到的是1,但线程02把值刷回主内存,此时A=2,然后线程01执行结束将1刷回主内存,而此时主内存的值从2变成了1),我们将变量A用volatile后,线程操作变量之后,不用等到刷新回写这一步,立即就能直接将A的值刷回主内存,从而保证了变量A对其他内存的可见性。我们再次试验,会发现变量A还是不会等于100.这也就说明了volatile关键字,并不能保证原子性。单靠volatile关键字无法解决数据不一致的问题。(我们可以使用synchronized关键字,它不但能保证可见性,还能保证原子性

个人建议:在只有读或者写的操作的情况下建议使用volatile关键字修饰,这样可以提高一些并发时候的效率问题。对于一些一已经被加锁的变量则不需要加volatile关键字,因为这样无法发挥出它的优势。

 static volatile Integer  a=new Integer(0);
    static Integer  b=new Integer(0);

    public static void main(String[] args) {
        testHaveVolatileDemo();//使用volati修饰
        testNoVolatileDemo();//没有使用volati修饰
    }

    public static void testHaveVolatileDemo() {
        for (int i = 0; i < 100; i++) {
            Thread thread = new Thread(new thread01());
            thread.setName("thread" + i);
            thread.start();
        }
        System.out.println("A=" + a);
    }
    public static void testNoVolatileDemo() {
        for (int i = 0; i < 100; i++) {
            Thread thread = new Thread(new thread02());
            thread.setName("thread" + i);
            thread.start();
        }
        System.out.println("B=" + b);
    }


    static class thread01 implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "  read A =" + a);
            a++;
        }
    }

    static class thread02 implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + "  read B =" + b);
            b++;
        }
    }
;