public class Test
{
static
{
i=0;
System.out.println(i);//這句編譯器會報錯:Cannot reference a field before it is defined(非法向前應用)
}
static int i=3;
public static void main(String[] args) {
}
}
}
爲何在靜態塊時輸出時會報錯的原因。其實這個時候i已經分配好內存並賦值爲3了,由下面代碼可以證明。先運行實例化對象,再輸出類對象的參數成功。
public class statictest {
static{
b=3;
}
static statictest st = new statictest();
statictest() {
system.out.println(b);
}
public static int b=10;
public static void main(String[] args) {
}
}
load(載入):作用於工作內存的變量,它把read操作從主內存中得到的變量值放入工作內存的變量副本中。
- assign(賦值):作用於工作內存的變量,它把一個從執行引擎接收到的值賦給工作內存的變量,每當虛擬機遇到一個給變量賦值的字節碼指令時執行這個操作。
問題在於java內存模型,不允許一個工作內存中直接使用一個未被初始化的變量,當開始b申請了空間初始化爲0後,在類加載的初始化階段,順序執行靜態語句時候,
已經將b=0從主內存複製到了工作內存,執行b=3 (修改工作內存),不允許一個線程丟棄它的最近的assign操作,即變量在工作內存中改變了之後必須把該變化同步回主內存。但是沒有完成提交到主內存完成assign和從主內存讀load操作,故輸出錯誤。
但是在執行內實例化前,會將工作內存中所有數據同步到主內存,故滿足了規則一個新的變量只能在主內存中“誕生”,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量,換句話說,就是對一個變量實施use、store操作之前,必須先執行過了assign和load操作。
8種基本操作規則:
不允許read和load、store和write操作之一單獨出現,即不允許一個變量從主內存讀取了但工作內存不接受,或者從工作內存發起回寫了但主內存不接受的情況出現。
不允許一個線程丟棄它的最近的assign操作,即變量在工作內存中改變了之後必須把該變化同步回主內存。
不允許一個線程無原因地(沒有發生過任何assign操作)把數據從線程的工作內存同步回主內存中。
-
- 一個新的變量只能在主內存中“誕生”,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量,換句話說,就是對一個變量實施use、store操作之前,必須先執行過了assign和load操作。
一個新的變量只能在主內存中“誕生”,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量,換句話說,就是對一個變量實施use、store操作之前,必須先執行過了assign和load操作。
一個變量在同一個時刻只允許一條線程對其進行lock操作,但lock操作可以被同一條線程重複執行多次,多次執行lock後,只有執行相同次數的unlock操作,變量纔會被解鎖。
如果對一個變量執行lock操作,那將會清空工作內存中此變量的值,在執行引擎使用這個變量前,需要重新執行load或assign操作初始化變量的值。
如果一個變量事先沒有被lock操作鎖定,那就不允許對它執行unlock操作,也不允許去unlock一個被其他線程鎖定住的變量。
對一個變量執行unlock操作之前,必須先把此變量同步回主內存中(執行store、write操作)。
這8種內存訪問操作以及上述規則限定,再加上稍後介紹的對volatile的一些特殊規定,就已經完全確定了Java程序中哪些內存訪問操作在併發下是安全的。由於這種定義相當嚴謹但又十分煩瑣,實踐起來很麻煩,所以在後文將介紹這種定義的一個等效判斷原則——先行發生原則,用來確定一個訪問在併發環境下是否安全。
注:
基於理解難度和嚴謹性考慮,最新的JSR-133文檔中,已經放棄採用這8種操作去定義Java內存模型的訪問協議了(僅是描述方式改變了,Java內存模型並沒有改變)。