Android中的同步與Mutex
多執行緒應用中,我們往往會對同一物件或類進行操作,這時我們需要應用同步鎖,以保證程式的正常執行。本文將從Synchronized, wait, notify這些Java常見的關鍵字/函式作為出發點,總結同步與鎖的問題,適合Java初級者閱讀解惑。
一. synchronized關鍵字。
為啥同步?簡單來講,一個執行緒在對某物件操作時,不想被其他執行緒的同步方法所幹擾。
在實際程式設計中,我們有兩種方式實現同步,分別是同步方法(synchronized methods)或同步塊(synchronized block/synchronized statement)。
同步方法是在方法前加synchronized, 如果該方法是static的,則認為鎖是相對於Class的,其他執行緒操作該類的任何物件時,遇到static同步方法或者方法內同步該Class時,需要等待;若該方法不是static的,則認為鎖是相對於自身物件(this)的,其他執行緒操作此物件時,遇到同步方法(非static),需要等待。
同步塊一般寫在函式裡,形式如下:
synchronized ( Expression ) Block
Expression需是引用型別(物件,類), Block則是程式碼塊。同步塊開始時需要獲得Expression的一個互斥鎖(mutual-exclusion lock)。當該鎖沒被其他執行緒佔用時,獲取該鎖並則執行Block裡內容,在Block結束後釋放此鎖。在執行Block時,任何其他想要獲得該鎖的執行緒需要阻塞等待。
[注意點]
可以認為鎖是屬於引用型別的, 同步的操作需要獲取鎖之後才進行,否則一直等待。程式設計時需注意鎖(synchronized)的物件
執行緒在wait後會釋放持有的鎖。有關wait詳細說明參見本文第二部分。
各執行緒同步時遵守先觸發,先得鎖原則(happens-before relationship)。
建構函式無法被synchronized。
二. wait(), notify(), notifyAll()
wait, notify, notifyAll方法均定義在基類Object中,它們的職責是為了在多執行緒中,可以有效的進行執行緒間互動,或者控制轉移。一個執行緒執行了wait, 需要其他執行緒notify,或者notifyAll, 才可以繼續執行。舉個例子,消費者和生產者,當沒有Message被生產者生產時,消費者則一直處於wait狀態,直到生產者生產了一條Message,然後notify消費者進行消費。
[注意點]
- wait, notify, notifyAll必須在synchronized程式碼內。即該執行緒持有了某引用的鎖時,wait, notify, notifyAll才可以被執行,否則,會報IllegalMonitorStateException
如以下程式碼(針對wait的,notify, notifyAll同理):
1.// 會拋IllegalMonitorStateException異常, 因為沒持有this的鎖。
2.this.wait();
3.
4.synchronized(this) {
5.
6. // 正確寫法
7. this.wait();
8.
9. // 會拋IllegalMonitorStateException異常, 因為沒有持有a物件的鎖。
10. this.a.wait();
11.}
執行wait()後會釋放鎖持有的鎖,其他等待同步中的執行緒這時會持有該鎖,並執行。
wait()執行後,執行緒狀態會變為disabled, 想繼續執行除非以下事件中的一個發生:
a)其他執行緒在此同步的引用上執行了notify()或者notifyAll(),注意是和wait相同引用上執行的notify()。
b)執行緒被其他執行緒中斷,會報InterruptedException。
c)wait可以指定timeout, 當timeout時間過去時。
wait繼續執行需要重新獲得該引用的鎖,若有其他執行緒佔有著此鎖,則仍然無法恢復。
notify是隨機喚醒一個wait中的執行緒,notifyAll是把所有wait中的執行緒全部喚醒。notify的物件仍舊是其持有的鎖的引用。
如果沒有執行緒wait, notify將會被忽略。
下面在給大家看一個互斥量的實現程式碼
01.public class Mutex
02.{
03. private boolean syncLock;
04.
05. ////////////////////////////////////////////////
06. // Constructor
07. ////////////////////////////////////////////////
08.
09. public Mutex()
10. {
11. syncLock = false;
12. }
13.
14. ////////////////////////////////////////////////
15. // lock
16. ////////////////////////////////////////////////
17.
18. public synchronized void lock()
19. {
20. while(syncLock == true) {
21. try {
22. wait();
23. }
24. catch (Exception e) {
25. Debug.warning(e);
26. };
27. }
28. syncLock = true;
29. }
30.
31. public synchronized void unlock()
32. {
33. syncLock = false;
34. notifyAll();
35. }
36.
37.}
在你需要互斥的地方,就可以用這個類物件的方法了