• java中volatile关键字


    转自:

      http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html

      http://www.ibm.com/developerworks/cn/java/j-jtp06197.html

    synchronized 

    同步块大家都比较熟悉,通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用

    synchronized 修饰的方法 或者 代码块。

    volatile

    用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最的值。volatile很容易被误用,用来进行原子性操作。

     1 public class Counter {
     2     public static int count = 0;
     3  
     4     public static void inc() {
     5         //这里延迟1毫秒,使得结果明显
     6         try {
     7             Thread.sleep(1);
     8         } catch (InterruptedException e) {
     9         }
    10  
    11         count++;
    12     }
    13  
    14     public static void main(String[] args) {
    15         //同时启动1000个线程,去进行i++计算,看看实际结果
    16         for (int i = 0; i < 1000; i++) {
    17             new Thread(new Runnable() {
    18                 @Override
    19                 public void run() {
    20                     Counter.inc();
    21                 }
    22             }).start();
    23         }
    24  
    25         //这里每次运行的值都有可能不同,可能为1000
    26         System.out.println("运行结果:Counter.count=" + Counter.count);
    27     }
    28 }

     

    在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,

    线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存

    变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,

    在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图

    描述这写交互

    java volatile1

    read and load 从主存复制变量到当前工作内存
    use and assign  执行代码,改变共享变量值
    store and write 用工作内存数据刷新主存相关内容

    其中use and assign 可以多次出现

    但是这一些操作并不是原子性,也就是 在read load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样

    对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的

    例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值

    在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6

    线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6

    导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

    Volatile 变量

    Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。

    出于简易性或可伸缩性的考虑,您可能倾向于使用 volatile 变量而不是锁。当使用 volatile 变量而非锁时,某些习惯用法(idiom)更加易于编码和阅读。此外,volatile 变量不会像锁那样造成线程阻塞,因此也很少造成可伸缩性问题。在某些情况下,如果读操作远远大于写操作,volatile 变量还可以提供优于锁的性能优势。

    正确使用 volatile 变量的条件

    您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:

    • 对变量的写操作不依赖于当前值。
    • 该变量没有包含在具有其他变量的不变式中。

  • 相关阅读:
    tensorflow2中pydot问题
    tensorflow2的差异总结
    tensorflow2.0中引入keras和原来的keras的差异
    Linux 后台任务进程管理工具supervisor的使用
    【Go学习】go 原生库net/http发送 GET POST 请求
    正则校验crontab格式
    【FastAPI 学习十二】定时任务篇
    【FastAPI 学习 十一】 项目目录结构demo(自己改版)
    【FastAPI 学习 十】使用Redis
    【FastAPI 学习 九】图片文件上传
  • 原文地址:https://www.cnblogs.com/ylz8401/p/6078597.html
Copyright © 2020-2023  润新知