天天看點

violate關鍵字使用場景

先看一段代碼:

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不會為每個工作線程進行緩存。