wordpress換電腦網(wǎng)站排名優(yōu)化教程
可見性:
什么是可見性:
可見性是指在數(shù)據(jù)在收到一個線程的修改時,其他的線程也可以得知并獲取修改后的值的屬性。這是并發(fā)編程的三大特性之一。
為了提高cpu的利用率,cpu在獲取數(shù)據(jù)時,不是直接在主內(nèi)存讀取數(shù)據(jù),而是在告訴緩存里面,但是在多核CPU下,每個CPU的高速緩存是獨立的,也就造成了如果CPU修改自己高速緩存的內(nèi)容,但是數(shù)據(jù)沒有同步給主內(nèi)存或這是其他緩存,就造成了數(shù)據(jù)的不一致。
cpu的高速緩存:
-
CPU為了提高速率會首先在高速緩存里面查找數(shù)據(jù),如果高速緩存里面沒有數(shù)據(jù),再去主內(nèi)存里面查找數(shù)據(jù)。
-
從緩存1到緩存3,存儲的內(nèi)容主鍵變大,但是查詢速度變小。
圖解可見性問題:
在初始階段,我們的i=0 ,但是CPU1將數(shù)據(jù)改為1,如果沒有可見性,主內(nèi)存和其他的告訴緩存并不會知道數(shù)據(jù)改變,這樣的話,計算的結(jié)構(gòu)就會出現(xiàn)錯誤。
代碼演示可見性問題
//演示可見性問題就是在修改了一個值之后,我們的另一個現(xiàn)成的數(shù)據(jù)并沒有改變。
如果沒有可見性的問題,那么只要是修改一個值的話,這個之就會被所有的使用者感知到他的變化,不會出現(xiàn)程序中的狀況
如何解決可見性問題
1.volatile
-
當(dāng)變量被volatile修飾之后這個變量的讀寫都會變得很特別,對于可見性來說,被他修飾的變量的改變可以被任意的使用這個變量的線程感知。
-
volatile的讀操作:在讀取被volatile修飾的變量是,CPU會直接在主內(nèi)存模塊去讀取,在高速緩存中的變量的值會被標(biāo)記為不可用,所以每個CPU讀取的值都是相同的。
-
volatile的寫操作:對于volatile修飾的變量的寫操作,他會將緩存中的修改的共享變量的值及時的刷新到主內(nèi)存。
添加了volatile的變量再轉(zhuǎn)為匯編語言時,會追加一個指令,這個指令就是規(guī)定了使用volatile的變量在寫入時是直接寫入主內(nèi)存,并且這個臨界變量在緩存中的值都不在有效,需要在主內(nèi)存中重新讀取。
2.synchronized
在程序添加synchronized關(guān)鍵字之后,會將CPU高速緩存中將帶有synchronized關(guān)鍵字的代碼塊和方法里面的變量移除。重新在主內(nèi)存中加載。在釋放所之后,將這個變量直接同步到主內(nèi)存。
在這里小編要提醒一下:變量移除時值得所有CPU緩存的變量都會被移除。
3.ReentranLock
我們在講解這個之前不如去看一下ReentranLock的源碼:在源碼里面我們發(fā)現(xiàn)他其實時使用了volatile關(guān)鍵字。
4.final
在只是進行讀取的數(shù)據(jù)里面是不會發(fā)生可見性到問題的,也就是說如果變量被修飾的話,因為是不可變的,也就是不會發(fā)生可見性問題。
提示:在java里面,volitile是不能與共同修飾一個變量的。
有序性:
CPU在執(zhí)行指令時為了讓指令可以快速地執(zhí)行,會進行指令重排,但是指令重排之后的代碼會出現(xiàn)有序性的問題。
有序性定義:
大家都知道我們的java是亂序執(zhí)行的。但是在多線程的時候,亂序執(zhí)行就會造成數(shù)據(jù)的問題。下面小編用代碼演示一下:
代碼演示有序性:
private static int a,b,x,y;public static void main(String[] args) throws InterruptedException {for (int i = 0; i < Integer.MAX_VALUE; i++) {a = 0;b = 0;x = 0;y = 0;
?Thread t1 = new Thread(() -> {a = 1;x = b;});Thread t2 = new Thread(() -> {b = 1;y = a;});
?t1.start();t2.start();t1.join();t2.join();
?if (x == 0 && y == 0) {System.out.println("第" + i + "次,x = " + x + ",y = " + y);}}}
在這段代碼中,可能會出現(xiàn)輸出每個都是零的時候,證明了java會指令重排。
解決有序性問題:
have before :
具體規(guī)則:
-
單線程happen-before原則:在同一個線程中,書寫在前面的操作happen-before后面的操作。
-
鎖的happen-before原則:同一個鎖的unlock操作happen-before此鎖的lock操作。
-
volatile的happen-before原則: 對一個volatile變量的寫操作happen-before對此變量的任意操作。
-
happen-before的傳遞性原則: 如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
-
線程啟動的happen-before原則:同一個線程的start方法happen-before此線程的其它方法。
-
線程中斷的happen-before原則:對線程interrupt方法的調(diào)用happen-before被中斷線程的檢測到中斷發(fā)送的代碼。
-
線程終結(jié)的happen-before原則:線程中的所有操作都happen-before線程的終止檢測。
-
對象創(chuàng)建的happen-before原則:一個對象的初始化完成先于他的finalize方法調(diào)用。
volatile
volatile可以解決可見性和有序性問題,他是通過內(nèi)存屏障的方式來解決指令重拍的問題。在兩個指令之間加上一個指令,避免進行指令的重新排序。