static 和 volatile 关键字

  • 在多线程中使用同一个静态方法时,每个线程使用各自的实例字段(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很容易被误用,用来进行原子性操作。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 从三月份找实习到现在,面了一些公司,挂了不少,但最终还是拿到小米、百度、阿里、京东、新浪、CVTE、乐视家的研发岗...
    时芥蓝阅读 42,872评论 11 349
  • Java8张图 11、字符串不变性 12、equals()方法、hashCode()方法的区别 13、...
    Miley_MOJIE阅读 3,910评论 0 11
  • 头发丝:80um 视网膜:200um 深度学习不是人工智能,只是一个聚类算法。 蛇形机器人: 可以,救灾,盗墓(开...
    YOUNG_FAN阅读 1,205评论 0 49
  • 前天删了一段通话录音。录音里的那个女孩带着尴尬却又假装轻松的口吻和一个男孩诉说这些年自己对男孩的情感。女孩从初中说...
    诗媚阅读 470评论 5 2
  • 小时候的每天早上,床起了我,脸洗了我。 早餐来吃我。 鸡蛋敲碎我的壳,包子不喜欢我的馅料, 还好,牛奶一直觉得我很...
    蔡千嬅阅读 466评论 0 3

友情链接更多精彩内容