美文网首页
static 和 volatile 关键字

static 和 volatile 关键字

作者: MicoCube | 来源:发表于2018-03-08 12:51 被阅读0次
    • 在多线程中使用同一个静态方法时,每个线程使用各自的实例字段(instance field)的副本,而共享一个静态字段(static field)。所以说,如果该静态方法不去操作一个静态成员,只在方法内部使用实例字段(instance field),不会引起安全性问题。但是,如果该静态方法操作了一个静态字段,则需要静态方法中采用互斥访问的方式进行安全处理。也就是说当某个静态类的静态方法使用了synchronized时实际上是对这个方法里的全局静态变量进行线程互斥保护。

    • volatile同步机制不同于synchronized, 前者是内存同步,后者不仅包含内存同步(一致性),且保证线程互斥(互斥性)。

    • 变量放在主存区上,使用该变量的每个线程,都将从主存区拷贝一份到自己的工作区上进行操作。

    • volatile, 声明这个字段易变(可能被多个线程使用),Java内存模型负责各个线程的工作区与主存区的该字段的值保持同步,即一致性。

    • static, 声明这个字段是静态的(可能被多个实例共享),在主存区上该类的所有实例的该字段为同一个变量,即唯一性。

    • volatile, 声明变量值的一致性;static,声明变量的唯一性。

    • volatile同步机制不同于synchronized, 前者是内存同步,后者不仅包含内存同步(一致性),且保证线程互斥(互斥性)。

    • static 只是声明变量在主存上的唯一性,不能保证工作区与主存区变量值的一致性;除非变量的值是不可变的,即再加上final的修饰符,否则static声明的变量,不是线程安全的。

    • 既然static已经是全局唯一了,那应该就是线程间共享,因此一个线程改变它其它线程都能看到,可惜这并不是事实,为了提高优化度和运行速度,一个线程修改这个变量是不一定立即写回内存让其它线程看到的,这时候就需要用volatile强制要求每次改变都写回内存了

    • static其实跟线程没太大关系,应该说对多个对象实例是可见的。你说对多个线程可见,虽然没什么毛病,因为静态变量全局可见嘛,但是把这个理解转到线程的上线文中是困惑的起因。

    • “volatile保证了线程之间的可见性”:因为线程看到volatile变量会去读取主内存最新的值,而不是自个一直在那跟内部的变量副本玩,所以保证了valatile变量在各个线程间的可见性。

    • volatile 可以保证线程间的可见性,是指一个线程中对变量操作了,另一个线程能马上看见这次改变,不会访问到旧数据(没有被volatile 修饰的变量往往会因为优化的原因,或者是多cpu的原因导致其他线程访问到了旧的数据)。

    • static 静态部分 在类加载的时候会在类对象保留一份,此类的实例间共享一份静态资源,并加载到内存,寄存器,缓存,不保证每个线程看到的值是一致(假设有线程对静态变量修改的话);而volatile表示每次保证读取到到是主内存中最新的值。可能也是你想问的多线程中分别两种修饰符的变量的可见性,是否保证一致。

    • 下面摘自Java语言规范(Java Language Specification)的官方解释:

        1. If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created.
        1. A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable。
    • 每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存,变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。


      image.png

      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很容易被误用,用来进行原子性操作。

    相关文章

      网友评论

          本文标题:static 和 volatile 关键字

          本文链接:https://www.haomeiwen.com/subject/krxyfftx.html