用在多線程,同步變量。 線程為了提高效率,将某成員變量(如a)拷貝了一份(如b),線程中對a的通路其實通路的是b。隻在某些動作時才進行a和b的同步。是以存在a和b不一緻的情況。volatile就是用來避免這種情況的。volatile告訴jvm, 它所修飾的變量不保留拷貝,直接通路主記憶體中的(也就是上面說的a)
在java記憶體模型中,有main memory,每個線程也有自己的memory (例如寄存器)。為了性能,一個線程會在自己的memory中保持要通路的變量的副本。這樣就會出現同一個變量在某個瞬間,在一個線程的memory中的值可能與另外一個線程memory中的值,或者main memory中的值不一緻的情況。
一個變量聲明為volatile,就意味着這個變量是随時會被其他線程修改的,是以不能将它cache線上程memory中。以下例子展現了volatile的作用:
假如pleasestop沒有被聲明為volatile,線程執行run的時候檢查的是自己的副本,就不能及時得知其他線程已經調用tellmetostop()修改了pleasestop的值。
volatile一般情況下不能代替sychronized,因為volatile不能保證操作的原子性,即使隻是i++,實際上也是由多個原子操作組成:read i; inc; write i,假如多個線程同時執行i++,volatile隻能保證他們操作的i是同一塊記憶體,但依然可能出現寫入髒資料的情況。如果配合java 5增加的atomic wrapper classes,對它們的increase之類的操作就不需要sychronized。
volatile關鍵字相信了解java多線程的讀者都很清楚它的作用。volatile關鍵字用于聲明簡單類型變量,如int、float、 boolean等資料類型。如果這些簡單資料類型聲明為volatile,對它們的操作就會變成原子級别的。但這有一定的限制。例如,下面的例子中的n就不是原子級别的:
如果對n的操作是原子級别的,最後輸出的結果應該為n=1000,而在執行上面積代碼時,很多時侯輸出的n都小于1000,這說明n=n+1不是原子級别的操作。原因是聲明為volatile的簡單變量如果目前值由該變量以前的值相關,那麼volatile關鍵字不起作用,也就是說如下的表達式都不是原子操作:
n++;
n = n + 1;
如果要想使這種情況變成原子操作,需要使用synchronized關鍵字,如上的代碼可以改成如下的形式:
上面的代碼将n=n+1改成了inc(),其中inc方法使用了synchronized關鍵字進行方法同步。是以,在使用volatile關鍵字時要慎重,并不是隻要簡單類型變量使用volatile修飾,對這個變量的所有操作都是原來操作,當變量的值由自身的上一個決定時,如n=n+1、n++ 等,volatile關鍵字将失效,隻有當變量的值和自身上一個值無關時對該變量的操作才是原子級别的,如n = m + 1,這個就是原級别的。是以在使用volatile關鍵時一定要謹慎,如果自己沒有把握,可以使用synchronized來代替volatile。
1、在兩個或者更多的線程通路的成員變量上使用volatile。當要通路的變量已在synchronized代碼塊中,或者為常量時,不必使用。
2、由于使用volatile屏蔽掉了vm中必要的代碼優化,是以在效率上比較低,是以一定在必要時才使用此關鍵字。