[CF1439A2] Binary Table (Hard Version) - 構造
volatile關鍵字
學習材料來源於網路
如有侵權,聯絡刪除
volatile關鍵字
可見性問題:讓一個執行緒對共享變數的修改,能夠及時的被其他執行緒看到。
根據JMM中規定的happen before和同步原則:
對某個volatile欄位的寫操作 happens-before每個後續對該volatile欄位的讀操作。對volatile變數v的寫入,與所有其他執行緒後續對v的讀同步
要滿足這些條件,所以volatile關鍵字就有這些功能:
1.禁止快取;
volatile變數的訪問控制符會加個ACC_VOLATILE
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.5
2.對volatile變數相關的指令不做重排序;
示例1
public class VisibilityDemo { private volatile boolean flag = true; public static void main(String[] args) throws InterruptedException { VisibilityDemo visibilityDemo = new VisibilityDemo() ; new Thread(new Runnable(){ @Override public void run() { int i = 0; while (visibilityDemo.flag) { i++; } System.out.println(i); } }).start() ; TimeUnit. SECONDS. sleep( 2); visibilityDemo.flag = false; //設定is為false,使上面的執行緒結束wh ile迴圈demo1.flag = false; System. out.println("被置為false了."); } }
final在JMM中的處理
- final在該物件的建構函式中設定物件的欄位,當執行緒看到該物件時,將始終看到該物件的final欄位的正確構造版本。虛擬碼示例: f= new finalDemo();讀取到的f.x一定最新,x為final欄位。
- 如果在建構函式中設定欄位後發生讀取,則會看到該final欄位分配的值,否則它將看到預設值;虛擬碼示例: public finalDemo(){x= l;y=x;}; y會等於1;
- 讀取該共享物件的final成員變數之前,先要讀取共享物件。
虛擬碼示例:r=new ReferenceObj(); k= r.f;這兩個操作不能重排序 - 通常static final是不可以修改的欄位。然而System.in,System.out和System.err是static final欄位,遺留原因,必須允許通過set方法改變,我們將這些欄位稱為防寫,以區別於普通final欄位;
Word Tearing位元組處理
一個欄位或元素的更新不得與任何其他欄位或元素的讀取或更新互動。
特別是,分別更新位元組陣列的相鄰元素的兩個執行緒不得干涉或互動,也不需要同步以確保順序一致性。
有些處理器(尤其是早期的Alphas處理器)沒有提供寫單個位元組的功能。
在這樣的處理器上更新 byte陣列,若只是簡單地讀取整個內容,更新對應的位元組,然後將整個內容再寫回記憶體,將是不合法的。
這個問題有時候被稱為“字分裂(word tearing)”,在單獨更新單個位元組有難度的處理器上,就需要尋求其它方式了。
基本不需要考慮這個,瞭解就好。
public class WordTearing extends Thread {
static final int LENGTH = 8;
static final int ITERS = 1000000;
static byte[] counts = new byte[LENGTH];
static Thread[] threads = new Thread[LENGTH];
final int id;
WordTearing(int i) {
id = i;
}
public void run() {
byte v = 0;
for (int i = 0; i < ITERS; i++) {
byte v2 = counts[id];
if (v != v2) {
System.err.println("Word-Tearing found: " +
"counts[" + id + "] = "+ v2 +
", should be " + v);
return;
}
v++;
counts[id] = v;
}
}
public static void main(String[] args) {
for (int i = 0; i < LENGTH; ++i)
(threads[i] = new WordTearing(i)).start();
}
}
double和long的特殊處理
虛擬機器規範中,寫64位的double和long分成了兩次32位值的操作
由於不是原子操作,可能導致讀取到某次寫操作中64位的前32位,以及另外一次寫操作的後32位
讀寫volatile 的 long和double總是原子的。讀寫引用也總是原子的
商業JVM不會存在這個問題,雖然規範沒要求實現原子性,但是考慮到實際應用,大部分都實現了原子性。