final 关键字与安全发布 多线程中篇(十三)

final的一般理解

在Java中,final关键字能够用来修饰类、方法和变量(包括成员变量和局部变量)
你们应该都知道final表示最终的、最后的含义,也就是不能在继续
修饰类表示不能继承,修饰方法表示不能重写,修饰变量表示不能修改
image_5c6f5971_3826
当用final修饰一个类时,代表这个类不能被继承。也就是说,若是一个类你永远不会让他被继承,就能够用final进行修饰
注意:final类中的全部成员方法都会被隐式地指定为final方法(也能够认为不可以继承就是由于全部的方法你都不能继承,因此所有方法隐式final)
当final修饰方法时,表示此方法不能被重写,也就是不能被子类覆盖(可是绝不影响多个重载的final方法,重载和重写不是一个概念)
注意:
若是是final而且private的方法,子类是看不到private的,因此若是子类新写了一个看似“重写”的方法,实际上是属于子类的新方法,这并非重写了final方法
当final修饰变量时,至关于一个只读变量,只能进行读取,而不能进行设置,若是是成员变量那么须要在赋值时或者构造方法中对他进行设置。
局部变量必须是定义时,参数列表中的就是参数传递时,其余时候不能再进行更改了
综上,final的一般认知就是这些,表示最终的、最后的、不可变得,能够用于定义类、方法、变量
 
其实final还有另外的做用,那就是安全发布对象的一种方法
什么是安全发布?

安全发布

两个关键字“发布”“安全”
所谓发布通俗一点的理解就是建立一个对象,使这个对象能被当前范围以外的代码所使用
好比Object o = new Object();
而后接下来使用对象o
可是对于普通变量的建立,以前分析过,大体分为三个步骤:
  • 分配内存空间
  • 将o指向分配的内存空间
  • 调用构造函数来初始化对象
这三个步骤不是原子的,若是执行到第二步,尚未进行初始化,此时对象已经不是null了,若是被其余代码访问,这将收获一个错误的结果。
或者说对象还没有彻底建立就被使用了,其余线程看到的结果多是不一致的
这就是 不安全的发布
根本缘由就是JVM建立对象的过程涉及到分配空间、指针设置、数据初始化等步骤,并非同步的,涉及到主存与缓存、处理器与寄存器等,可见性没办法获得保障
 
因此说,什么是安全发布,简单理解就是对象的建立可以保障在被别人使用前,已经完成了数据的构造设置,或者说一个对象在使用时,已经完成了初始化。
不幸的是,Java对此并无进行保障,你须要本身进行保障,好比synchronized关键字,原子性、排他性就能够作到这一点
怎么保障安全发布?有几种方法:
一种是刚才提到的锁机制,经过加锁能够保障中间状态不会被读取
另外还有:
  • 借助于volatile或者AtomicReference声明对象
  • 借助于final关键字
  • 在静态初始化块中,进行初始化(JVM会保障)
很显然,对于锁机制,那些线程安全的容器好比ConcurrentMap,也是知足这条的,因此也是安全发布  

final与安全发布

对于final,当你建立一个对象时,使用final关键字可以使得另外一个线程不会访问处处于“部分建立”的对象
由于:当构造函数退出时,final字段的值保证对访问构造对象的其余线程可见
若是某个成员是final的,JVM规范作出以下明确的保证:
一旦对象引用对其余线程可见,则其final成员也必须正确的赋值
因此说借助于final,就如同你对对象的建立访问加锁了通常,自然的就保障了对象的安全发布。
若是你不但愿后续被继承、重写、更改,你应该尽量的将他们声明为final
一篇很不错的文章:
对于普通的变量,对象的内存空间分配、指针设置、数据初始化,和将这个变量的引用赋值给另外一个引用,之间是可能发生重排序的,因此也就致使了其余线程可能读取到不一致的中间状态
可是对于final修饰的变量,JVM会保障顺序
不会在对final变量的写操做完成以前,与将变量引用赋值给其余变量之间进行重排序,也就是final变量的设置完成始终会在被读取以前  

总结

final除了不可变的定义以外,还与线程安全发布息息相关
借助于final,能够达到对象安全发布的保障,只须要借助于final,不在须要任何额外的付出,他可以保障在多线程环境下,老是可以读取到正确的初始化的值
因此,若是你不但愿变量后续被修改,你应该老是使用final关键字
并且,很显然在某些场景下,final也能够解决必定的安全问题
相关文章
相关标签/搜索