volatile
volatile是一個(gè)類型修飾符(type specifier),就像大家更熟悉的const一樣,它是被設(shè)計(jì)用來(lái)修飾被不同線程訪問(wèn)和修改的變量。volatile的作用是作為指令關(guān)鍵字,確保本條指令不會(huì)因編譯器的優(yōu)化而省略,且要求每次直接讀值。
volatile的變量是說(shuō)這變量可能會(huì)被意想不到地改變,這樣,編譯器就不會(huì)去假設(shè)這個(gè)變量的值了。
Java
是一門面向?qū)ο?a target="_blank">編程語(yǔ)言,不僅吸收了C++語(yǔ)言的各種優(yōu)點(diǎn),還摒棄了C++里難以理解的多繼承、指針等概念,因此Java語(yǔ)言具有功能強(qiáng)大和簡(jiǎn)單易用兩個(gè)特征。Java語(yǔ)言作為靜態(tài)面向?qū)ο缶幊陶Z(yǔ)言的代表,極好地實(shí)現(xiàn)了面向?qū)ο罄碚摚试S程序員以優(yōu)雅的思維方式進(jìn)行復(fù)雜的編程。
Java具有簡(jiǎn)單性、面向?qū)ο?、分布式、健壯性、安全性、?**立與可移植性、多線程、動(dòng)態(tài)性等特點(diǎn)。Java可以編寫(xiě)桌面應(yīng)用程序、Web應(yīng)用程序、分布式系統(tǒng)和嵌入式系統(tǒng)應(yīng)用程序等
java之用volatile和不用volatile的區(qū)別
在當(dāng)前的Java內(nèi)存模型下,線程可以把變量保存在本地內(nèi)存(比如機(jī)器的寄存器)中,而不是直接在主存中進(jìn)行讀寫(xiě)。這就可能造成一個(gè)線程在主存中修改了一個(gè)變量的值,而另外一個(gè)線程還繼續(xù)使用它在寄存器中的變量值的拷貝,造成數(shù)據(jù)的不一致。
要解決這個(gè)問(wèn)題,只需要像在本程序中的這樣,把該變量聲明為volatile(不穩(wěn)定的)即可,這就指示JVM,這個(gè)變量是不穩(wěn)定的,每次使用它都到主存中進(jìn)行讀取。一般說(shuō)來(lái),多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志都應(yīng)該加volatile修飾。
Volatile修飾的成員變量在每次被線程訪問(wèn)時(shí),都強(qiáng)迫從共享內(nèi)存中重讀該成員變量的值。而且,當(dāng)成員變量發(fā)生變化時(shí),強(qiáng)迫線程將變化值回寫(xiě)到共享內(nèi)存。這樣在任何時(shí)刻,兩個(gè)不同的線程總是看到某個(gè)成員變量的同一個(gè)值。
用volatile和不用volatile的區(qū)別,運(yùn)行一下,就知道了。
不用volatile:
?。踛ava] view plain copypackage com.keyword;
public class TestWithoutVolatile {
private static boolean bChanged;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
for (;;) {
if (bChanged == !bChanged) {
System.out.println(“!=”);
System.exit(0);
}
}
}
}.start();
Thread.sleep(1);
new Thread() {
@Override
public void run() {
for (;;) {
bChanged = !bChanged;
}
}
}.start();
}
}
運(yùn)行后,程序進(jìn)入死循環(huán)了,一直在運(yùn)行。
用volatile:
?。踛ava] view plain copypackage com.keyword;
public class TestWithVolatile {
private static volatile boolean bChanged;
public static void main(String[] args) throws InterruptedException {
new Thread() {
@Override
public void run() {
for (;;) {
if (bChanged == !bChanged) {
System.out.println(“!=”);
System.exit(0);
}
}
}
}.start();
Thread.sleep(1);
new Thread() {
@Override
public void run() {
for (;;) {
bChanged = !bChanged;
}
}
}.start();
}
}
程序輸出!=,然后馬上退出。
但是,很多情況下,用不用volatile,感覺(jué)不出什么區(qū)別,什么時(shí)候要用volatile呢?看看JDK里使用volatile的類。
比如java.util.regex.Pattern里的變量:
[java] view plain copyprivate transient volatile boolean compiled = false;
還有,java.lang.System的變量:
?。踛ava] view plain copyprivate static volatile Console cons = null;
一般就是初始化的時(shí)候,需要用到volatile。
java.util.Scanner里的變量,如:
?。踛ava] view plain copyprivate static volatile Pattern boolPattern;
private static volatile Pattern separatorPattern;
private static volatile Pattern linePattern;
初始化boolPattern的代碼:
?。踛ava] view plain copyprivate static Pattern boolPattern() {
Pattern bp = boolPattern;
if (bp == null)
boolPattern = bp = Pattern.compile(BOOLEAN_PATTERN,
Pattern.CASE_INSENSITIVE);
return bp;
}
上面的情況,可以使用synchronized來(lái)對(duì)boolPattern加鎖,但是synchronized開(kāi)銷比volatile大,volatile能夠勝任上面的工作。
volatile不保證原子操作,所以,很容易讀到臟數(shù)據(jù)。
使用建議:在兩個(gè)或者更多的線程訪問(wèn)的成員變量上使用volatile。當(dāng)要訪問(wèn)的變量已在synchronized代碼塊中,或者為常量時(shí),不必使用。
評(píng)論