Posted on 2010-01-14 14:36
瘋狂 閱讀(1097)
評(píng)論(0) 編輯 收藏 所屬分類(lèi):
java
為了確保可以在線程之間以受控方式共享數(shù)據(jù),Java 語(yǔ)言提供了兩個(gè)關(guān)鍵字:synchronized
和 volatile
。
Synchronized
有兩個(gè)重要含義:它確保了一次只有一個(gè)線程可以執(zhí)行代碼的受保護(hù)部分(互斥,mutual exclusion 或者說(shuō) mutex),而且它確保了一個(gè)線程更改的數(shù)據(jù)對(duì)于其它線程是可見(jiàn)的(更改的可見(jiàn)性)。
如果沒(méi)有同步,數(shù)據(jù)很容易就處于不一致?tīng)顟B(tài)。例如,如果一個(gè)線程正在更新兩個(gè)相關(guān)值(比如,粒子的位置和速率),而另一個(gè)線程正在讀取這兩個(gè)值,有可能在第一個(gè)線程只寫(xiě)了一個(gè)值,還沒(méi)有寫(xiě)另一個(gè)值的時(shí)候,調(diào)度第二個(gè)線程運(yùn)行,這樣它就會(huì)看到一個(gè)舊值和一個(gè)新值。同步讓我們可以定義必須原子地運(yùn)行的代碼塊,這樣對(duì)于其他線程而言,它們要么都執(zhí)行,要么都不執(zhí)行。
同步的原子執(zhí)行或互斥方面類(lèi)似于其它操作環(huán)境中的臨界段的概念。
同步可以讓我們確保線程看到一致的內(nèi)存視圖。
處理器可以使用高速緩存加速對(duì)內(nèi)存的訪問(wèn)(或者編譯器可以將值存儲(chǔ)到寄存器中以便進(jìn)行更快的訪問(wèn))。在一些多處理器體系結(jié)構(gòu)上,如果在一個(gè)處理器的高速緩存中修改了內(nèi)存位置,沒(méi)有必要讓其它處理器看到這一修改,直到刷新了寫(xiě)入器的高速緩存并且使讀取器的高速緩存無(wú)效。
這表示在這樣的系統(tǒng)上,對(duì)于同一變量,在兩個(gè)不同處理器上執(zhí)行的兩個(gè)線程可能會(huì)看到兩個(gè)不同的值!這聽(tīng)起來(lái)很?chē)樔耍?em>卻很常見(jiàn)。它只是表示在訪問(wèn)其它線程使用或修改的數(shù)據(jù)時(shí),必須遵循某些規(guī)則。
Volatile
比同步更簡(jiǎn)單,只適合于控制對(duì)基本變量(整數(shù)、布爾變量等)的單個(gè)實(shí)例的訪問(wèn)。當(dāng)一個(gè)變量被聲明成 volatile
,任何對(duì)該變量的寫(xiě)操作都會(huì)繞過(guò)高速緩存,直接寫(xiě)入主內(nèi)存,而任何對(duì)該變量的讀取也都繞過(guò)高速緩存,直接取自主內(nèi)存。這表示所有線程在任何時(shí)候看到的 volatile
變量值都相同。
如果沒(méi)有正確的同步,線程可能會(huì)看到舊的變量值,或者引起其它形式的數(shù)據(jù)損壞。
簡(jiǎn)單的同步示例 |
第 6 頁(yè)(共12 頁(yè)) |
使用 synchronized
塊可以讓您將一組相關(guān)更新作為一個(gè)集合來(lái)執(zhí)行,而不必?fù)?dān)心其它線程中斷或看到計(jì)算的中間結(jié)果。以下示例代碼將打印“1 0”或“0 1”。如果沒(méi)有同步,它還會(huì)打印“1 1”(或“0 0”,隨便您信不信)。
public class SyncExample {
private static lockObject = new Object();
private static class Thread1 extends Thread {
public void run() {
synchronized (lockObject) {
x = y = 0;
System.out.println(x);
}
}
}
private static class Thread2 extends Thread {
public void run() {
synchronized (lockObject) {
x = y = 1;
System.out.println(y);
}
}
}
public static void main(String[] args) {
new Thread1().run();
new Thread2().run();
}
}
在這兩個(gè)線程中都必須使用同步,以便使這個(gè)程序正確工作。
Volatile
對(duì)于確保每個(gè)線程看到最新的變量值非常有用,但有時(shí)我們需要保護(hù)比較大的代碼片段,如涉及更新多個(gè)變量的片段。
同步使用監(jiān)控器(monitor)或鎖的概念,以協(xié)調(diào)對(duì)特定代碼塊的訪問(wèn)。
每個(gè) Java 對(duì)象都有一個(gè)相關(guān)的鎖。同一時(shí)間只能有一個(gè)線程持有 Java 鎖。當(dāng)線程進(jìn)入 synchronized
代碼塊時(shí),線程會(huì)阻塞并等待,直到鎖可用,當(dāng)它可用時(shí),就會(huì)獲得這個(gè)鎖,然后執(zhí)行代碼塊。當(dāng)控制退出受保護(hù)的代碼塊時(shí),即到達(dá)了代碼塊末尾或者拋出了沒(méi)有在 synchronized
塊中捕獲的異常時(shí),它就會(huì)釋放該鎖。
這樣,每次只有一個(gè)線程可以執(zhí)行受給定監(jiān)控器保護(hù)的代碼塊。從其它線程的角度看,該代碼塊可以看作是原子的,它要么全部執(zhí)行,要么根本不執(zhí)行。