標籤:volatile java 線程
在Java記憶體模型中,有main memory(主記憶體)還每個線程各自的線程記憶體memory(例如:寄存器)。為了效能一個線程會在自己memory中保持要訪問變數的副本。這樣就會出現同一個變數在某一個時刻一個線程記憶體中的值和其他線程記憶體或者主記憶體中的值不一致。
一個變數聲明為volatile,就意味著這個變數隨時會被其他線程修改,因此不能將他cahe線上程memory中,即:不會再memory中保留他的副本,下面看個例子:
public class TestVolatile extends Thread { private volatile boolean okay; public void run(){ while(!okay){ System.out.println("current time is okay"); } } /** * 修改通知變數 * @description * @author Jader * 2015年5月4日 下午5:18:33 * @version 1.0 */ public void timeToOkay(){ okay = true; }}
此時線程執行run方法的時候不會讀取自己的cahe的副本,而是回去主記憶體中讀取,當執行timetoOkay時會通知其他線程,其他線程會去主記憶體中擷取最新的okay。不知道我理解的是否正確
Volatile並不能保證操作的原子性,不像synchronized那樣。
最典型的例子是i++,他實際上是由多個原子操作組成,如果多個線程同時執行i++,volatile只能保證他們操作的同一塊記憶體,針對此可以使用java.util.concurrent.atomic.AtomicInteger下的
getAndIncrement()方法實現,具體可以查看API有關concurrent包下的並發類。
volatile一般用來修飾變數,而synchronized是用來修飾代碼塊或者方法,例如:
int i1;int geti1() {return i1;}
得到儲存在當前線程i1的值,多個線程有多個i1變數的拷貝,而且這些i1的值可以互不相同。上面我們已經提供,多線程環境中分為主記憶體和線程記憶體,每個線程都可以有他們自己的變數副本,而這個變數副本可以和主記憶體中的值不相同,當出現並發的時候線程記憶體中的i1和線程記憶體中的i1的值就不相同
volatile int i2;int geti2(){return i2;}
此時的i2的值是從主記憶體中擷取,使用volatile修飾後的變數不允許出現主記憶體和線程記憶體不一致的情況。任何一個線程改變了變數的值其他線程都會立即擷取到相同的值。
int i3;synchronized int geti3() {return i3;}
synchronized獲得並釋放監視器,如果兩個線程同時使用了同一個對象鎖,監聽器能強制保證代碼塊同一時刻只有一個線程能執行
此時geti3其實經過以下步驟:
1、線程請求獲得監視this對象的對象鎖(如果已經被鎖定就需要等待)
2、線程記憶體資料被清除,從主記憶體中讀入
3、執行代碼塊geti3()
4、對於變數的任何改動可以安全的寫到主記憶體中
5、線程釋放監視this對象的對象鎖
通過比較可以看出volatile只是線上程記憶體和主記憶體之間同步某個變數的值,而synchronized是通過鎖定和解鎖某個監視器同步所有的變數,要比volatile消耗更多的資源。
但是volatile關鍵字修飾的變數不能保證是原子的,諸如i++,i–等
總結:
1、volatile解決了線程間共用變數的可見問題
2、使用volatile會增加效能的開銷
3、volatile並不能解決線程同步問題
4、解決i++、i++這種不同步的問題需要使用synchronized、atomicInteger系列的包,也會增加效能開銷
Java中volatile關鍵字的作用