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也可以解決一定的安全問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM