一次偶然的機會(java多線程電梯做業尋求多個進程分享變量的方法),接觸到了volatile,所以我查閱了相關的材料,對這部分作了一些瞭解,在這裏和你們分享一下。java
首先,咱們先來聊一聊幾個概念編程
編譯器和JVM經過改變程序的處理順序來優化程序,用於提升程序性能的方式。緩存
多線程程序設計中,重排序會致使運行錯誤。多線程
舉個栗子:性能
class Compare {
private int x = 0;
private int y = 0;
public void write() {
x = 100;
y = 50;
}
public boolean compare() {
return x<y
}
}
public class Main {
public static void main(String[] args) {
final Compare com = new Compare();
new Thread() {
public void run() {
com.write();
}
}.start();
new Thread() {
public void run() {
com.read();
}
}.start();
}
}
讓人吃驚的是,x<y竟然真的存在true的狀況優化
緣由就在於重排序this
編譯器的優化策略可能會改變x,y的賦值順序,致使x<yatom
顯然,咱們能夠經過synchronized來解決這個問題spa
線程A將某個值寫入字段x,線程B讀到了這個值線程
多線程中的可見性問題來源於normal read/write操做是經過緩存在執行的,read到的不必定是最新值,write的也不必定當即對其餘線程可見
而synchronized是解決這一問題的有效方法,相信你們也並不陌生,具體用法能夠參考個人另外一篇博客
其實,volatile也是一種不錯的方法
不可分割的操做,例如某線程正在執行synchronized方法,其餘線程沒法進入該方法,從多線程的角度,這就是原子操做。
Java定義了一些原子操做:primitive type(char、int)的賦值和引用,對象等引用類型的賦值和引用
可是long與double的操做不是原子的,在線程共享時須要放入synchronized
Make sure that a given variable is read directly from main memory and always written back to main memory when updated
volatile具備同步處理(參考sunchronization)和對long和double的原子操做這兩種功能
一、若是線程A向volatile字段寫入的值對線程B可見,那麼以前向其餘字段寫入的值都是對B可見
二、向volatile字段讀取和寫入先後不會發生重排序
看到這裏,咱們發現重排序和可見性的問題好像都被volatile解決了
在這裏,咱們來看一段代碼,深刻理解一下同步處理
class TryVolatile {
private int num = 0;
private volatile boolean valid = false;
public void write() {
num = 1;
valid = true;
/*
*一、不會被重排序
*二、線程B中valid會爲true
*三、線程B可能出現num=1
*/
}
public void read() {
if (valid) {
System.out.println(this.num);
}
}
}
public class Main {
public static void main(String[] args) {
final tryVolatile = new TryVolatile;
new Thread() {
public void run() {
tryVolatile.write();
}
}.start();
new Thread() {
public void run() {
tryVolatie.read();
}
}.start();
}
}
總結一下volatile的使用:
一、volatile字段賦值語句位置很重要!!!(能夠運行上面的代碼觀察
二、volatile不會進行線程互斥處理(volatile字段不會進入等待隊列
三、訪問volatile字段會產生性能開銷(參考synchronized
java.util.concurrent.atomic包提供了原子操做編程的類,例如AtomicInteger、AtomicLong、AtomicIntegerArray等,都是經過封裝volatile獲得的