經(jīng)常訪問(wèn)的變量會(huì)從主存讀取到線程的高速緩沖區(qū),導(dǎo)致不同線程間對(duì)數(shù)據(jù)的修改不能及時(shí)同步:
import java.util.concurrent.TimeUnit;
class TObject{
public boolean b = false;
}
public class VisibilityTest {
static boolean flag = true;
static int num = 1;
static String s = "a";
static TObject tObject = new TObject();
static Object object;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while (flag) {
if (num==100) break;
if (s.equals("b")) break;
if (tObject.b) break;
if (object!=null) break;
// System.out.println("this can stop!");
}
});
t1.start();
TimeUnit.MILLISECONDS.sleep(10);
flag = false;
num = 100;
s = "b";
tObject.b = true;
object=new Object();
}
}
上方代碼直接運(yùn)行,t1線程始終無(wú)法讀到main線程對(duì)值的修改正常結(jié)束。
要想同步t1和main的值。
- 可以在變量前加volatile(易變的)關(guān)鍵字,使得每個(gè)線程對(duì)變量的訪問(wèn)不生成高速緩存,必須去主存讀取和修改(保證了可見(jiàn)性但降低了性能)。
- 遇到同步代碼塊或者同步方法(synchronized),也會(huì)更新線程私有高速緩沖內(nèi)存的值,println是一個(gè)同步方法,上方注釋//System.out.println("this can stop!");打開(kāi)會(huì)導(dǎo)致更新緩沖區(qū)的值。
- 若上方TimeUnit.MILLISECONDS.sleep(10); 修改為TimeUnit.MILLISECONDS.sleep(1);也會(huì)成功停止,因?yàn)楦咚倬彌_還未形成,t1還是從主存中去讀取的變量值。
注:
volatile和synchronized都可以實(shí)現(xiàn)線程間的可見(jiàn)性,但volatile沒(méi)有上鎖步驟,在實(shí)現(xiàn)可見(jiàn)性方面更輕量,但不能保證原子性。