在java开发过程中,final是大家常用的关键字,无非就是用来修饰类,方法和变量,来表名类不能被继承,方法不会被覆盖,变量不能被改变,悄悄的说一句,private方法也隐式的final。通过一段时间的学习,我想和大家分享一下final的内存语义。

在java并发编程的艺术中第三章这样描述过final的内存语义:

  1. 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给一个引用变量,这两个操作之间不能重排序。
  2. 初次读一个包含final域对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。

在初次读这两句话时,我是一脸懵逼,这个final到底用来干啥的?多读了几遍突然意识到,final类型的变量可以保证在多线程发布某个对象时,这个对象的final域变量能够被正常的初始化(在写final变量后加了storestore屏障,在读final变量前加了loadload屏障),而普通类型的变量可能不会被正确的初始化,这样导致该对象在多个线程之间出现不一致的情况,这也就是我们所说的引用溢出。罪魁祸首是处理器重排序,因为处理器重排序不会影响单线程语义,但会破坏多线程语义,导致发布对象处在一个不一致的状态。

举一个引用溢出的例子,大家倒背如流的双重判定的单例模式:

public class Singleton {
    private static Singleton uniqueInstance;
    private final String name;
    private Singleton(String name){
        this.name = name;
    }
    public static Singleton getInstance(String name){
        if(uniqueInstance == null){
            synchronized (Singleton.class){
                if(uniqueInstance == null){
                    uniqueInstance = new Singleton(name);
                }
            }
        }
        return uniqueInstance;
    }
}

有什么问题吗?相信细心的同学会说uniqueInstance应该用volatile修饰,但是大家有没有发现,我的这个Singleton有点不一样呢,多了一个final类型成员变量name。那个这个final类型的变量究竟有啥作用呢?

首先我先说一下大家平时用volatile时,这个volatile有什么作用吧?

public class Singleton {
    private volatile static Singleton uniqueInstance;
    private Singleton(){}
    public static Singleton getInstance(){
        if(uniqueInstance == null){
            synchronized (Singleton.class){
                if(uniqueInstance == null){
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

假设有A和B两个线程来调用Singleton.getInstance()方法,A先拿到锁,执行uniqueInstance=new Singleton()时,volatile可以阻止new Singeton()时重排序,那么B在得到对象时,是一个已经初始化ok的对象。假设上述没有volatile关键字,那么会出现uniqueInstance不为空,但对象还未初始化的情况,导致B线程得到的是一个未初始化的对象,造成不一致的情况。当然对于A线程来说,重排序并不影响uniqueInstance的使用。

那么为什么加了一个final类型的name就可以不需要用volatile呢?

我们可以回头看看final内存语义的第一条,uniqueInstance在被赋值前,保证final类型的变量会被正确初始化,显然B线程使用这个对象时,这个uniqueInstance会在一个一致的状态上,如果Singleton多了一个普通类型的变量,不加volatile会出现多线程问题。不加volatile仅仅适用于Singleton的所有成员变量是final类型的情况下,这样发布的对象会在各个线程间处在一个一致的状态。

当然,不加volatile这种写法是我自己凭空造出来的,只是结合final的语义来分析一下,如有错误,欢迎批评指正,大家共同前行。

说说final关键字(好像有干货)的更多相关文章

  1. 浅析Java中的final关键字(转载)

    自http://www.cnblogs.com/dolphin0520/p/3736238.html转载 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括 ...

  2. 浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  3. 浅析final 关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下 ...

  4. [转载]浅析Java中的final关键字

    浅析Java中的final关键字 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来 ...

  5. 转载:浅析Java中的final关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下 ...

  6. java中的final关键字

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下 ...

  7. 浅析Java中的final关键字(转)

    谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法.下 ...

  8. java基础4:深入理解final关键字

    本文主要介绍了final关键字的使用方法及原理 具体代码在我的GitHub中可以找到 https://github.com/h2pl/MyTech 文章首发于我的个人博客: https://h2pl. ...

  9. 浅析Java中的final关键字--转

    转载自:http://www.importnew.com/18586.html#comment-581628 谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关 ...

随机推荐

  1. Python基础(十三)-进程&线程

    进程 定义:进程是程序在一个数据集合上的运行过程,是系统进行资源分配和调度的基本单位 线程

  2. chrome下li标签onclick事件无效

    //绑定事件 $(document).ready(function () { $("ul").children().click(function () { clickLi(this ...

  3. CVE-2016-0143 漏洞分析(2016.4)

    CVE-2016-0143漏洞分析 0x00 背景 4月20日,Nils Sommer在exploitdb上爆出了一枚新的Windows内核漏洞PoC.该漏洞影响所有版本的Windows操作系统,攻击 ...

  4. sql convert() 函数

    convert: 时间格式转换为其他时间格式的函数 CONVERT ( data_type [ ( length ) ] , expression [ , style ] )   data_type: ...

  5. VMware安装Centos6.8设置ip无法远程连接问题

    今天使用VMware安装Centos6.8minimal版本再设置ip地址的时候遇到了一些麻烦,就是无法ping通Centos操作系统的配置的ip从而无法用Xshell远程连接上. 如何配置请看下面的 ...

  6. Android5.1源码Xposed框架编译

    介绍 Xposed框架是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作 . 对于Android5.1系统,官方提供 ...

  7. 判断是否点击键盘enter键

    //enter键监测触发事件 function checkKeyCode() { document.onkeydown = function(e) { if(!e) e = window.event; ...

  8. js跨域请求获得数据

    很多时候我们想访问其它站点下的数据怎么办? 由于javascript语言安全限制即同源策略造成的. 在使用ajax请求访问其他服务器的数据,此时客户端会出现跨域问题. 在js中,我们直接用XMLHtt ...

  9. L2-012. 关于堆的判断

    L2-012. 关于堆的判断 题目链接:https://www.patest.cn/contests/gplt/L2-012 终于ac了,简直要哭.题目还是很简单的,不过很多坑: 1.寻找x下标时,有 ...

  10. Windows后渗透

    My 命令行下收集主机信息 使用wmic识别安装到系统中的补丁情况: wmic qfe get description,installedOn 识别正在运行的服务: sc query type= se ...