zoukankan      html  css  js  c++  java
  • 2.3.4volatile的原子性

    关键字volatile虽然增加了实例变量在多个线程之间的可见性,但它却不具备同步性,那么也不具备原子性。

    测试

    package com.cky.thread;
    
    /**
     * Created by edison on 2017/12/9.
     */
    public class MyThread extends Thread{
        volatile  public static  int count;
        private  static void addCount() {
            for(int i=0;i<100;i++) {
                count++;
            }
            System.out.println("count="+count);
        }
    
        @Override
        public void run() {
            super.run();
            addCount();
        }
    }
    package com.cky.test;
    
    import com.cky.thread.MyThread;
    
    /**
     * Created by edison on 2017/12/9.
     */
    public class Run {
        public static void main(String[] args) {
    
            MyThread[] myThreads = new MyThread[100];
            for (int i = 0; i < 100; i++) {
                myThreads[i] = new MyThread();
    
            }
            for (int i = 0; i < 100; i++) {
                myThreads[i].start();
            }
        }
    }
    count=8600
    count=8500
    count=8400
    count=8200
    count=8300
    count=8000
    count=7800
    count=7700
    count=7500

    结果:线程异步

    更改加上关键字synchronized

    package com.cky.thread;
    
    /**
     * Created by edison on 2017/12/9.
     */
    public class MyThread extends Thread{
        volatile  public static  int count;
        synchronized private  static void addCount() {
            for(int i=0;i<100;i++) {
                count++;
            }
            System.out.println("count="+count);
        }
    
        @Override
        public void run() {
            super.run();
            addCount();
        }
    }
    count=9400
    count=9500
    count=9600
    count=9700
    count=9800
    count=9900
    count=10000

    在本例中,如果在方法private static void addCount()前加入synchronized同步关键字,也就没有必要再使用volatile关键字来声明count变量了。

    关键字volatile主要使用的场合是在多个线程中可以感知实例变量被更改了,并且可以获得最新的值的使用,也就是多线程读取共享变量时可以获得最新值的使用。

    关键字volatile提示线程每次从共享变量中读取变量,而不是从私有内存中读取,这样就保证了同步数据的可见性,但在这里需要注意的是

    如果修改实例变量中的数据,例如i++,也就是i=i+1,则这样的操作其实不是一个原子操作,也就是非线程安全,表达式i++操作步骤

    1)从内存中取出i值

    2)计算i值

    3)将i值写到内存中

    假设在第2步计算值的时候,另外一个线程也修改i值,那么这个时候会出现脏读,解决方法就是使用synchronized关键字,所以volatile本身并不处理数据的

    原子性,而是强制对数据的读写及时影响到主内存。

    如下图演示的是volatile出现非线程安全的原因

     结论:

    1)read 和load阶段:从主内存复制变量到当前线程工作内存

    2)use和assign阶段:执行代码,改变共享变量值

    3)store和write阶段:用工作内存数据刷新主内存对应的变量的值

    在多线程环境中。use和assign是多次出现的,但这个操作不是原子性,也就是在read和load之后,如果主内存中的count变量发生修饰之后,线程

    工作内存中的值已经加载,不会产生对应的变化,也就是私有内存和公共内存中的值不同步,所以计算出来的结果和预期不一样,也就出现了线程不安全

    对于volatitle修饰的变量,JVM只是保证从主内存加到线程工作内存中的值是最新的,例如线程1和线程2在进行read和load操作时,发现主内存中的

    count值都是5,那么都会加载这个最新值,也就是说,volatile关键字解决的是变量读时的可见性问题,但无法保证原子性,对于多线程访问同一个实例变量

    还是需要加锁同步。

  • 相关阅读:
    Ubuntu 安装.net core 设置源仓储地址
    ASP.NET Core开发-MVC 使用dotnet 命令创建Controller和View
    ubuntu安装postman
    redis相关操作
    C# StringValues 类型
    MySQL时间戳与日期格式的相互转换
    C# 生成时间戳以及时间戳转换为时间
    关于vscode 使用nuget插件相关说明
    ubuntu一些命令
    使用HBuilderX新建Uniapp项目并在不同平台上运行调试
  • 原文地址:https://www.cnblogs.com/edison20161121/p/8011212.html
Copyright © 2011-2022 走看看