在Java5中,final关键字是非常重要而事实上却经常被忽视其作为同步的作用。本质上讲,final能够做出如下保证:当你创建一个对象时,使用final关键字能够使得另一个线程不会访问到处于“部分创建”的对象,否则是会可能发生的。这是 因为,当用作对象的一个属性时,final有着如下的语义:

当构造函数结束时,final类型的值是被保证其他线程访问该对象时,它们的值是可见的

为什么是必须的

使用final是所谓的安全发布(safe publication)的一种方式,这里,发布(publication)一个地相意味着在一个线程中创建它,同时另一个线程在之后的某时刻可以引用到该新创建的对象。当JVM调用对象的构造函数时,它必须将各成员赋值,同时存储一个指向该对象的指针。就像其他任何的数据写入一样,这可能是乱序的,and their application to main memory can be delayed and other processors can be delayed unless you take special steps to combat this(看不太懂,是不是说“把他们写回主存可能推迟,并且其他的处理器(看到变化)也会推迟,要客服这一点,除非采取非常步骤”)。特别的,指向对象的引用可能在成员变量提交之前(导致如此的原因之一是编译器的指令重排ordering:if you think about how you'd write things in a low-level language such as C or assembler, it's quite natural to store a pointer to a block of memory, and then advance the pointer as you're writing data to that block)就被写入到主存并被访问到了。这样会导致另一个线程看到了一个不合法或不完整的对象。

而final可以防止此类事情的发生:如果某个成员是final的,JVM规范做出如下明确的保证:一旦对象引用对其他线程可见,则其final成员也必须正确的赋值了。

final的对象引用

对象的final成员成员的值在当退出构造函数时,他们也是最新的。这意味着:

final类型的成员变量的值,包括那些用final引用指向的collections的对象,是读线程安全而无需使用synchronization的

注意,如果你有一个指向collection,数组或其他可变对象的final引用,如果存在其他线程访问,仍然需要使用同步机制来访问该对象(或使用ConcurrentHashMap)。

因此,不可变对象(指所有的成员都是final并且成员要么是基本类型,要么指向另一个不可变对象)可以并发访问而无需使用同步机制。通过final引用读取“实际不可变”对象(指成员虽然实际并不是final,然而却从不会改变)也是安全的。然而,从程序设计的角度来看,在此种情况下强化不可变性是明智的(如用Collections.unmodifiableList()封装一个collection)。That way, you'll spot bugs introduced when one of your colleagues naughtily attempts to modify a collection that you didn't intend to be modified!

 使用final的限制条件和局限性

当声明一个final成员时,必须在构造函数退出前设置它的值,如下:

public class MyClass {
private final int myField = 3;
public MyClass() {
...
}
}

或者

public class MyClass {
private final int myField;
public MyClass() {
...
myField = 3;
...
}
}

需要强调的是将指向对象的成员声明为final只能将该引用设为不可变的,而非所指的对象。例如如果一个list声明如下:

private final List myList = new ArrayList();

仍然可以修改该list

myList.add("Hello");

然而,声明为final可以保证如下操作不合法:

myList = new ArrayList();
myList = someOtherList;

什么时候应该使用final

一个答案就是“尽可能的使用”。任何你不希望改变的(基本类型,或者指向一个对象,不管该对象是否可变)一般来讲都应该声明为final。另一种看待此问题的方式是:

如果一个对象将会在多个线程中访问并且你并没有将其成员声明为final,则必须提供其他方式保证线程安全

“其他方式”可以包括声明成员为volatile,使用synchronized或者显式Lock控制所有该成员的访问。

大家往往忽视的典型case是在一个线程创建一个对象,而后在另一个线程使用,如一个通过ThreadPoolExecutor的对象。这种情况下,必须保证该对象的线程安全性:这和线程的并发访问关系不大,主要是因为在其生命周期内,不同的线程会在任意时刻访问它(还是内存模型的问题吧)

关于java中final关键字与线程安全性的更多相关文章

  1. Java中final关键字修饰变量、方法、类的含义是什么

    Java中的关键字final修饰变量.方法.类分别表示什么含义? 先看一个简单的介绍 修饰对象 解释说明 备注 类 无子类,不可以被继承,更不可能被重写. final类中的方法默认是final的 方法 ...

  2. 安卓开发(Java)中关于final关键字与线程安全性

    前言 学习新知识固然重要,但是时常往回看看,温故知新是很必要的.回顾一下线程安全性和final关键字. 正文 从Java 5开始,final keyword一个特殊用法是在并发库中一个非常重要且经常被 ...

  3. java中final 关键字的作用

    final 关键字的作用 java中的final关键字可以用来声明成员变量.本地变量.类.方法,并且经常和static一起使用声明常量. final关键字的含义: final在Java中是一个保留的关 ...

  4. 关于Java中final关键字的详细介绍

    Java中的final关键字非常重要,它可以应用于类.方法以及变量.这篇文章中我将带你看看什么是final关键字?将变量,方法和类声明为final代表了什么?使用final的好处是什么?最后也有一些使 ...

  5. 解析Java中final关键字的各种用法

    首先,我们可以从字面上理解一下final这个英文单词的中文含义:“最后的,最终的; 决定性的; 不可更改的:”.显然,final关键词如果用中文来解释,“不可更改的”更为合适.当你在编写程序,可能会遇 ...

  6. java中final关键字的使用方法

     [java中为什么会有final变量]: final这个关键字的含义是"这是无法改变的"或者"终态的": 那么为什么要阻止改变呢? java语言的发明者可 ...

  7. Java中primitive type的线程安全性

    Java中primite type,如char,integer,bool之类的,它们的读写操作都是atomic的,但是有几个例外: long和double类型不是atomic的,因为long和doub ...

  8. java 中final关键字

    1.final变量,一旦该变量被设定,就不可以再改变该变量的值. final关键字定义的变量必须声明时赋值.一旦一个对象引用被修饰为final后,它只能恒定指向一个对象,一个既是static和fina ...

  9. Java中final关键字概述

    使用final修饰过的变量都不可以改变: 1.final修饰变量 恒定不变的属性,可以用final关键字来修饰: 变量名建议全部使用大写 final修饰的变量不能改变,如果程序中重新赋值,编译报错 例 ...

随机推荐

  1. 我和Ajax的故事

    我和Ajax结缘是在2015年的3月份,当时的项目需要Ajax技术来实现,但对于我来说完全是全新的名词,自己就上网上查找相关资料,结局很明显,知道概念但是具体的是什么东西根本傻傻不明白,后来这个技术是 ...

  2. CSS性能分析,如何优化CSS提高性能

    不负十年后的自己,共勉! 前端性能优化一直是一个比较热门的话题,我们总是在尽我们最大的努力去,提高我们的页面性能,比如减少HTTP请求,利用工具对资源进行合并压缩,脚本置底,避免重复请求,css sp ...

  3. ArcGIS工具之ET GeoWizards、GeoTools、GeoTools

    简介 ET GeoWizards是ET SpatialTechniques一套基于ArcGIS的工具集,从2002年开始,其设计的初衷: (1)让ArcView用户拥有ArcEditor甚至ArcIn ...

  4. centos虚拟机复制移动后网络配置无效

    移植Centos虚拟机后无法联网解决1.迁移以后,会存在其中一个网卡无法启动(eth0 or eth1) [root@ ~]# ifup eth0 WARNING: Deprecated config ...

  5. iOS之2016面试题一

    序言 招聘高峰期来了,大家都非常积极地准备着跳槽,那么去一家公司面试就会有一堆新鲜的问题,可能不会,也可能会,但是了解不够深.本篇文章为群里的小伙伴们去宝库公司的笔试题,由笔者整理并提供笔者个人参考答 ...

  6. iOS点击推送消息跳到应用指定页面

    现在的推送用的越来越频繁,几乎每个应用都开始用到了.其实又有几个用户会去看推送消息呢?没办法,产品经理最大啊,只是苦了我们这一帮程序员啊!闲话少说,进入正题.兄弟我用的是极光推送,自然是以极光推送为例 ...

  7. Mac Security工具使用总结

    Security是Mac系统中钥匙串和安全模块的命令行管理工具,(图形化工具为Keychain Access.app).钥匙串(Keychain)实质上就是一个用于存放证书.密钥.密码等安全认证实体的 ...

  8. Android自定义控件(二)

    这一篇主要来讲一下自定义控件中的自定义viewgroup,我们以项目中最常用的下拉刷新和加载更多组件为例 简单介绍一下自定义viewgroup时应该怎么做. 分析:下拉刷新和加载更多的原理和步骤 自定 ...

  9. iOS 获取相册中图片的名字 url

    __block NSString *imageFileName; NSURL *imageURL = [info valueForKey:UIImagePickerControllerReferenc ...

  10. 学习Maven之Maven Enforcer Plugin

    1.Maven Enforcer plugin是什么鬼? 在说这个插件是什么前我们先思考这么一个问题:当我们开发人员进入项目组进行开发前,要准备开发环境,而领导总是会强调工具的统一,编译环境的统一.比 ...