先看一段代碼:
package com.fsun.research.thread.violate;
public class MainTest {
private static boolean ready;
private static class CounterThread implements Runnable{
@Override
public void run() {
while(!ready){
}
}
}
public static void main(String[] args) {
new Thread(new CounterThread()).start();
System.out.println("主線程睡眠300毫秒");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
ready=true;
}
}
上面的例子中run方法會陷入到死循環中,這是因為CounterThread線程在主線程改變ready值之前已經從主記憶體中讀取了ready的值到自己的工作記憶體(主線程睡眠300毫秒就是為了保證這一點),由于在while循環中不停大量的循環讀取,jvm為了提高讀取效率,對于這種高并發讀取的情況是從線程的工作記憶體來讀取,即使當主線程睡眠醒來改變了ready的值并更新了主記憶體但卻并沒有什麼用。
再看下面一個例子:
package com.fsun.research.thread.violate;
public class MainTest {
private static boolean ready;
private static class CounterThread implements Runnable{
@Override
public void run() {
System.out.println("ready====="+ready);
try {
Thread.sleep(1000); //睡眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("1秒後讀取到ready的值"+ready);
}
}
public static void main(String[] args) {
new Thread(new CounterThread()).start();
System.out.println("主線程睡眠300毫秒");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
ready=true;
}
}
主線程睡眠300毫秒為了保證CounterThread在主線程更新ready之前從主記憶體中讀取到ready的值。CounterThread在睡眠1秒之後又讀取ready的值,此時讀取到了主線程更新過後的值。為了使程式的執行流程可控加入了線程休眠,實際上在測試的過程中不加入線程休眠,隻要線程不在短時間内頻繁的讀取ready的值都沒有出現讀取到的ready的值錯誤的情況。
可見在高并發的情況下,對于共享變量jvm并不能保證每個線程讀取到的值都是一樣的。為了解決這個問題,jvm引入了violate關鍵字,使用violate關鍵字來進行聲明的變量,每次都會從主記憶體讀取,jvm不會為每個工作線程進行緩存。