zoukankan      html  css  js  c++  java
  • Java并发拾遗(五)——final

    final也是并发中一个常用的tips。JMM中对final提供的重排序规则如下:

    1、当在constructor中对final变量写入之前,不会将构造对象的引用给出去。

    2、读一个包含final域的对象的引用 happens before 后续对这个final域的读

     关于(1),是说当在constructor内包含对final的写时,虽然constructor不是原子的,但在JMM会在对final的写之后,在constructor return之前插入一条内存屏障,这样就保证了在constructor将对象给到另一个线程的时候,是一定能看见对final域的写的结果的。看代码:

    public class FinalExample {
        int i;
        final int j;
        static FinalExample obj;
    
        // constructor
        public void FinalExample () {
            i = 1;
            j = 2;
        }
    
        public static void writer () {
            obj = new FinalExample();
        }
    
        public static void reader () {
            FinalExample object = obj;
            int a = object.i;
            int b = object.j;
        }
    
    }  

    现假设一个线程A执行writer方法,另一个线程B随后开始执行read方法。假设变量j不是final的,那么A在调用write时,constructor会发生两次写操作,这个时候线程B来调用reader读字段的时候,由于constructor方法不是原子的,又没有任何同步手段来协调线程A与B的同步,这时候就会乱掉了,比如constructor两次赋值发生了重排序,或者只完成了一次赋值,线程B就拿到构造一半的对象进行读值了,等等并发问题了。

    但是,当把j变量定义为final时,就会发生一点点的变化了。

    (1)final能够保证,在对象引用被任意线程可见之前,对象的final域一定被正确初始化了。而实现这种保证的手段,就是通过内存屏障,保证final域的赋值不会被重排序到constructor return之后。

    (2)final能够保证,初次读对象引用于初次读该对象所包含的final域,不会发生重排序。所以在执行read方法时,final能够保证一定是先确实拿到了obj的引用之后,才能读obj中的final域j。

    而实际上,IDEA对于final的赋值只能在final类变量定义时,不能将final的赋值推迟到constructor里。

  • 相关阅读:
    在浏览器中浏览git上项目目录结构
    部署elasticsearch(三节点)集群+filebeat+kibana
    谷歌浏览器安装Elasticsearch-head 插件
    Logstash配置文件修改自动加载和指定目录进行启动
    使用Dbvisualizer 连接 Elasticsearch
    Elasticsearch常见用法-分布式集群
    Elasticsearch常见用法-入门
    Elastic Stack 7.5.0白金版永不过期
    配置 Nginx 反向代理 WebSocket
    ES7.3.0配置
  • 原文地址:https://www.cnblogs.com/dosmile/p/6772528.html
Copyright © 2011-2022 走看看