From: [Android教程] 淺談Java同步鎖(Android中的同步)
多線程應(yīng)用中,我們往往會對同一對象或類進行操作,這時我們需要應(yīng)用同步鎖,以保證程序的正常運行。本文將從Synchronized, wait, notify這些Java常見的關(guān)鍵字/函數(shù)作為出發(fā)點,總結(jié)同步與鎖的問題,適合Java初級者閱讀解惑。
synchronized 關(guān)鍵字
在實際編程中,我們有兩種方式實現(xiàn)同步,分別是同步方法(synchronized methods)或同步塊(synchronized block/synchronized statement)。
同步方法是在方法前加synchronized, 如果該方法是static的,則認(rèn)為鎖是相對于Class的,其他線程操作該類的任何對象時,遇到static同步方法或者方法內(nèi)同步該Class時,需要等待;若該方法不是static的,則認(rèn)為鎖是相對于自身對象(this)的,其他線程操作此對象時,遇到同步方法(非static),需要等待。
[注意點]
可以認(rèn)為鎖是屬于引用類型的, 同步的操作需要獲取鎖之后才進行,否則一直等待。編程時需注意鎖(synchronized)的對象。
線程在wait后會釋放持有的鎖。有關(guān)wait詳細(xì)說明參見本文第二部分。
各線程同步時遵守先觸發(fā),先得鎖原則(happens-before relationship)。
構(gòu)造函數(shù)無法被synchronized。
wait && notify
wait, notify, notifyAll方法均定義在基類Object中,它們的職責(zé)是為了在多線程中,可以有效的進行線程間交互,或者控制轉(zhuǎn)移。一個線程執(zhí)行了wait, 需要其他線程notify,或者notifyAll, 才可以繼續(xù)執(zhí)行。舉個例子,消費者和生產(chǎn)者,當(dāng)沒有Message被生產(chǎn)者生產(chǎn)時,消費者則一直處于wait狀態(tài),直到生產(chǎn)者生產(chǎn)了一條Message,然后notify消費者進行消費。
[注意點]
- wait, notify, notifyAll必須在synchronized代碼內(nèi)。即該線程持有了某引用的鎖時,wait, notify, notifyAll才可以被執(zhí)行,否則,會報IllegalMonitorStateException.
如以下代碼(針對wait的,notify, notifyAll同理):
// 會拋IllegalMonitorStateException異常, 因為沒持有this的鎖。
this.wait();
synchronized(this) {
// 正確寫法
this.wait();
// 會拋IllegalMonitorStateException異常, 因為沒有持有a對象的鎖。
this.a.wait();
}
執(zhí)行wait()后會釋放鎖持有的鎖,其他等待同步中的線程這時會持有該鎖,并執(zhí)行。
wait()執(zhí)行后,線程狀態(tài)會變?yōu)閐isabled, 想繼續(xù)執(zhí)行除非以下事件中的一個發(fā)生:
- 其他線程在此同步的引用上執(zhí)行了notify()或者notifyAll(),注意是和wait相同引用上執(zhí)行的notify()。
- 線程被其他線程中斷,會報InterruptedException。
- wait可以指定timeout, 當(dāng)timeout時間過去時。
wait繼續(xù)執(zhí)行需要重新獲得該引用的鎖,若有其他線程占有著此鎖,則仍然無法恢復(fù)。
notify是隨機喚醒一個wait中的線程,notifyAll是把所有wait中的線程全部喚醒。notify的對象仍舊是其持有的鎖的引用。
如果沒有線程wait, notify將會被忽略。