• Java的volatile


    volatile作用对象:Volatile只作用于共享变量。

    共享变量:在多个线程之间能够被共享的变量被称为共享变量。共享变量包括所有的实例变量,静态变量和数组元素。他们都被存放在堆内存中,

    volatile作用

    1、同步

    同synchronized相比(synchronized通常称为重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。

    如图,如果变量没有volatile关键字,那么A线程对该变量的改变存储在内存A,B变量不可知。

    将一个共享变量声明为volatile后,会有以下效应:

        1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的变量强制刷新到主内存中去;

        2.这个写会操作会导致其他线程中的缓存无效。

    非原子操作出现的问题:

    package test;
    
    import java.util.concurrent.CountDownLatch;
    
    /**
     * Created by chengxiao on 2017/3/18.
     */
    public class Counter {
        public static volatile int num = 0;
        //使用CountDownLatch来等待计算线程执行完
        static CountDownLatch countDownLatch = new CountDownLatch(30);
        public static void main(String []args) throws InterruptedException {
            //开启30个线程进行累加操作
            for(int i=0;i<30;i++){
                new Thread(){
                    public void run(){
                        for(int j=0;j<10000;j++){
                            num++;//自加操作
                        }
                        countDownLatch.countDown();
                    }
                }.start();
            }
            //等待计算线程执行完
            countDownLatch.await();
            System.out.println(num);
        }
    }

    得到的结果并不是300000,而是224291,原因是num++不是个原子性的操作,而是个复合操作(读取、加1、赋值)。所以,在多线程环境下,有可能线程A将num读取到本地内存中,此时其他线程可能已经将num增大了很多,线程A依然对过期的num进行自加,重新写到主存中。

    解决方法:通过使用Automic原子操作类

    /**
     * Created by chengxiao on 2017/3/18.
     */
    public class Counter {
      //使用原子操作类
        public static AtomicInteger num = new AtomicInteger(0);
        //使用CountDownLatch来等待计算线程执行完
        static CountDownLatch countDownLatch = new CountDownLatch(30);
        public static void main(String []args) throws InterruptedException {
            //开启30个线程进行累加操作
            for(int i=0;i<30;i++){
                new Thread(){
                    public void run(){
                        for(int j=0;j<10000;j++){
                            num.incrementAndGet();//原子性的num++,通过循环CAS方式
                        }
                        countDownLatch.countDown();
                    }
                }.start();
            }
            //等待计算线程执行完
            countDownLatch.await();
            System.out.println(num);
        }
    }

    2、禁止指令重排序优化

    重排序在多线程中可能存在的问题:

    public class TestVolatile {
        int a = 1;
        boolean status = false;
    
        /**
         * 状态切换为true
         */
        public void changeStatus(){
            a = 2;//1
            status = true;//2
        }
    
        /**
         * 若状态为true,则running。
         */
        public void run(){
            if(status){//3
                int b = a+1;//4
                System.out.println(b);
            }
        }
    }

    上述第1、2步由于不存在依赖关系,可能会被系统冲排序,从而不能保证第四步的b=3。

  • 相关阅读:
    自我介绍
    秋季学期总结
    第七周编程总结
    第六周作业
    第五周编程总结
    第四周编程总结
    第三周作业
    第二周作业
    抓老鼠啊~亏了还是赚了
    币值转换
  • 原文地址:https://www.cnblogs.com/qionglouyuyu/p/10882469.html
Copyright © 2020-2023  润新知