Java杂谈5——关键字final与volatile

Final关键字

  在Java语言中,随着语境的不一样final关键字所表明的语义会有一些细微的差别。总的来讲,final关键字表达的含义是“禁止修改”,这层有点相似于C++中的const关键字。之因此要采用final关键字,通常是会出于性能和设计层面的考虑。下文会具体讨论final关键字在不一样语境中的具体用法。html

Final + 属性

  用final关键字修饰的属性,对于Java编译器来讲就是一个“常量”。其特色是:1.具体的值在编译期间就已经被肯定;2.在运行时不能再被修改。基于以上两个特色,咱们分别分析一下Java中具体的基本类型和引用类型:java

  对于基本类型,其自己就存放于虚拟机栈内部,因为这些基本类型都是与底层数据类型直接对应的,一些肯定的计算过程能够直接在编译期完成,优化了运行期的执行效率。c++

  对于引用类型,咱们已知引用自己其实也是存放于虚拟机栈中,final关键字只限制了对这个引用的更改,并不会限制对引用所指的实例化对象的变动。缓存

综上所述,咱们能够看出,final关键在修饰属性时,不论属性类型,限制修改的范围是固定在虚拟机栈内部的存储。安全

空白(blank)final多线程

  一个final属性能够定义的时候不赋予初始的值,可是在其实际使用以前一定须要被初始化,一般final属性的初始化,只会位于构造函数中或者属性定义时的表达式表达式。函数

Final+方法

  若是一个方法若是被声明为final类型,包含了两层含义:1.这个方法不能再被重写(即方法自己不能再被修改);2.方法的调用过程采用内嵌机制,更为高效(节约了入栈出栈的开销,更为高效,此时的final语义上有点相似于c++中的inline函数)。性能

  须要注意的是,private关键字是不能和final连用的。若是一个父类包含了private final修饰的方法,根据private关键字的语义,子类中应该是能够从新定义一个与父类中对应方法签名一直的方法,可是final关键字的语义有说明了这个方法是不能被重写的,产生了歧义。优化

Final修饰的形参spa

  若是一个方法的形参用final关键字修饰,表示的含义就是在这个方法内部,这个形参的值是不能被修改的。有点相似于final属性,若是final修饰的形参数据类型是一个引用,这里只限制了这个引用自己被更改,并不会限制对引用指向实例化对象的修改。

Final+类

         一个类用final关键字修饰,其含义包括:1.这个类不能再被任何子类继承,2.这个类内部的全部方法都默认是final方法


 

Volatile关键字

  Volatile的英文含义为易变的、挥发的,在java语言中volatile关键字是一个类型修饰符。JAVA中Volatile的做用:强制每次都直接读内存,阻止重排序,确保voltile类型的值一旦被写入缓存一定会被当即更新到主存

要真正理解volatile语义的做用,就必须优先搞懂volatile关键字相关的几个概念。

Java内存/线程模型

  在JAVA多线程环境下,每一个Java线程除了共享的虚拟机栈外和Java堆以外,还存在一个独立私有的堆内存(默认状况下大小为512KB,在线程被建立时分配,能够经过-Xss选项调节其默认值大小)。每一个线程独立运行,彼此之间都不可见,线程的私有堆内存中保留了一份主内存的拷贝,只有在特定需求的状况下才会与主存作交互(复制/刷新)。

JAVA内存模型示例图

重排序

         在有些场景下访问程序变量会表现出与程序制定的顺序不同。例如:编译器能够以优化的名义改变指令的顺序,处理器也能够不按照顺序来执行指令等。一个Java程序在从源代码到最终实际执行的指令序列之间,会经历一系列的重排序过程:

  这种任意改变指令顺序的行为在单线程的状况下“彷佛”是无害有益的,可是若是换到多线程的状况,因为全部线程都独立运行,不知道其余线程在作什么;若是多个线程批次之间还存在共享的内存区域,就必须解决同步的问题。

Volatile使用的理想条件

  正是因为JAVA中volatile关键字的这种语义,volatile能够被认为是一个轻量级的synchronized块,但实际使用过程当中仍是有一些限制。

  Volatile被正确使用的理想条件包括以下两点:

  • 对变量的写操做不依赖当前值。
  • 变量没有包含在具备其余变量的不变式中。

  说得很抽象,归结起来就是一个volatile变量可以用来实现“轻量级的同步”,前提是这个变量自身不能同时被多个线程共享修改。

  一般volatile的使用场景:存在多个线程同时运行,只有一个线程拥有对volatile属性的修改权,其余的线程只能进行读操做。具体的示例参考模式有兴趣的同窗能够参考文章《JAVA理论与实践》。

Volatile的注意事项

         Volatile只能保证每次线程读取到的变量来自最新的内存,保证修改操做可以及时反馈到主内存中,相对于synchronized锁定内存的作法,volatile在读取时拥有很大的性能上的优点。

Voltile关键字并不保证操做的原子性,例如一个自增操做i++,即便i被声明为voltile类型,在多线程的状况下,i的值也会是不肯定的。

         在JAVA中long类型和double类型都是64位长度,对于32位的机器这须要两次读内存的操做。在规范中,JAVA内存模型只在这种状况下保证用voilate修饰的double和long类型变量的两次读操做变为一个原子操做。

  综上所述,volatile关键字表现出了一种脆弱的同步机制,在没法确认当前场景绝对知足的前提下,voilate关键字很难保证安全合理得被使用。但确实volatile为咱们提供了在特定场景下更高的性能(相比synchronized)。

参考资料

  1. Java Memory Model http://www.cs.umd.edu/~pugh/java/memoryModel/
  2. 《Thinking in JAVA》
  3. JAVA理论与实践:正确使用volatile变量http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
  4. JSR 135 (JAVA Memory Model)FAQhttp://www.ticmy.com/?p=315
  5. JAVA线程/内存模型的缺陷与加强http://tech.163.com/06/0525/09/2HV7DCJ20009159T.html
  6. Wekipedia Java Memory Modelhttp://en.wikipedia.org/wiki/Java_Memory_Model
  7. 深刻理解Java内存模型http://www.infoq.com/cn/articles/java-memory-model-1
相关文章
相关标签/搜索