JAVA內(nèi)存模型和Happens-Before規(guī)則知識點講解
我們在本篇內(nèi)容里聊一聊JAVA的內(nèi)存模型和Happens-Before規(guī)則。
JAVA內(nèi)存模型
這里的JAVA內(nèi)存模型指的不是我們JVM專欄中提到的內(nèi)存分布模型,而是針對并發(fā)編程的,小伙伴們不要混淆概念了。
我們已經(jīng)知道,導致可見性問題的是緩存,導致有序性問題的是指令重排,那么禁用緩存和禁用指令重排不就可以避免出現(xiàn)這兩種問題了嗎。
但想想也知道,如果直接禁用掉,性能會大打折扣,所以正確的方式應該是按需禁用。
只有程序員才能分析出什么時候應該禁用,所以為了解決可見性和有序性,其實只要提供給程序員按需禁用的API接口就可以了。
JAVA的內(nèi)存模型是一個很復雜的規(guī)范,可以從不同的角度來解釋,本質(zhì)上我們可以理解成JAVA內(nèi)存模型規(guī)范了JVM如何按需禁用緩存和禁用指令重排。
具體來說這些方法包括 volatile、synchronized 和 final 等關鍵字,以及六項 Happens-Before 規(guī)則。
volatile不是JAVA獨有的關鍵字,它最開始的含義就是禁用CPU緩存,JAVA1.5之后對它進行了語義加強,就是引入了一套Happens-Before 規(guī)則。
例如下面的代碼:
class VolatileExample {
int x = 0;
volatile boolean v = false;
public void writer() {
x = 42;
v = true;
}
public void reader() {
if (v == true) {
// 這里 x 會是多少呢?
}
}
}
假如線程A執(zhí)行了writer方法,線程B執(zhí)行reader方法,如果線程B發(fā)現(xiàn)了v=true,那么同時也會發(fā)現(xiàn)x=42。
Happens-Before 規(guī)則
接下來我們就來看看今天的主角,Happens-Before是什么?
Happens-Before要表達的是:前面一個操作的結果對后續(xù)操作是可見的,它約束了編譯器的優(yōu)化行為,雖允許編譯器優(yōu)化導致的指令重排,但是要求編譯器優(yōu)化后一定遵守 Happens-Before 規(guī)則。
都說Happens-Before對于JAVA內(nèi)存模型來講是一個比較晦澀難懂的部分,但我們一點一點來剖析,其實沒那么難理解。
程序的順序性規(guī)則
這條規(guī)則是指在一個線程中,按照程序順序,前面的操作 Happens-Before 于后續(xù)的任意操作。
這條規(guī)則還是比較容易理解的,就是保證了單線程中程序的順序性。
volatile變量規(guī)則
這條規(guī)則是指對一個 volatile 變量的寫操作, Happens-Before 于后續(xù)對這個 volatile 變量的讀操作。
這么看的話,是不是發(fā)現(xiàn)其實它就是禁用CPU緩存的意思,多線程下保證變量的可見性。
傳遞性
這條規(guī)則是指如果 A Happens-Before B,且 B Happens-Before C,那么 A Happens-Before C。
這個傳遞性也很好理解,那么假如把傳遞性和volatile變量規(guī)則放在一起會發(fā)生什么呢?
就比如我們上文中的代碼,x=42 Happens-Before v=true,寫變量v=true Happens-Before 讀變量v,那么根據(jù)傳遞性規(guī)則,x=42 Happens-Before 讀變量v。
所以我們之前分析,如果線程B讀變量v=true,那么x=42對于線程B也是可見的。
并發(fā)工具包(java.util.concurrent)就是靠 volatile 語義來搞定可見性的,同時傳遞性也是對volatile關鍵字的增強,保證了可見性的同時也保證了有序性。
管程中鎖的規(guī)則
這條規(guī)則是指對一個鎖的解鎖 Happens-Before 于后續(xù)對這個鎖的加鎖。
這條規(guī)則其實也很容易理解,不加鎖何來解鎖一說。
線程start()規(guī)則
這條是關于線程啟動的。它是指主線程 A 啟動子線程 B 后,子線程 B 能夠看到主線程在啟動子線程 B 前的操作。
這條規(guī)則也沒什么好解釋的,就是字面意思。
線程join()規(guī)則
這條是關于線程等待的。它是指主線程 A 等待子線程 B 完成(主線程 A 通過調(diào)用子線程 B 的 join() 方法實現(xiàn)),當子線程 B 完成后(主線程 A 中 join() 方法返回),主線程能夠看到子線程的操作。當然所謂的“看到”,指的是對共享變量的操作。
總結
Java 的內(nèi)存模型是并發(fā)編程領域的一次重要創(chuàng)新,它主要分為兩部分,一部分面向編寫并發(fā)程序的應用開發(fā)人員,另一部分是面向 JVM 的實現(xiàn)人員的。
我們在并發(fā)專欄中理解前者就可以了。
到此這篇關于JAVA內(nèi)存模型和Happens-Before規(guī)則知識點講解的文章就介紹到這了,更多相關淺談JAVA內(nèi)存模型和Happens-Before規(guī)則內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot+MinIO實現(xiàn)對象存儲的示例詳解
MinIO?是一個基于Apache?License?v2.0開源協(xié)議的對象存儲服務,它是一個非常輕量的服務,可以很簡單的和其他應用的結合,所以下面我們就來看看SpringBoot如何整合MinIO實現(xiàn)對象存儲吧2023-10-10
如何解決創(chuàng)建maven工程時,產(chǎn)生“找不到插件的錯誤”問題
這篇文章主要介紹了如何解決創(chuàng)建maven工程時,產(chǎn)生“找不到插件的錯誤”問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
Springboot如何添加server.servlet.context-path相關使用
這篇文章主要介紹了Springboot如何添加server.servlet.context-path相關使用問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
JDK8 new ReentrantLock((true)加鎖流程
這篇文章主要介紹了java面試中常遇到的問題JDK8 new ReentrantLock((true)加鎖流程示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-07-07

