1. 程式人生 > >Android中的同步與Mutex

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時,任何其他想要獲得該鎖的執行緒需要阻塞等待。

[注意點]

  1. 可以認為鎖是屬於引用型別的, 同步的操作需要獲取鎖之後才進行,否則一直等待。程式設計時需注意鎖(synchronized)的物件

  2. 執行緒在wait後會釋放持有的鎖。有關wait詳細說明參見本文第二部分。

  3. 各執行緒同步時遵守先觸發,先得鎖原則(happens-before relationship)。

  4. 建構函式無法被synchronized。

二. wait(), notify(), notifyAll()

wait, notify, notifyAll方法均定義在基類Object中,它們的職責是為了在多執行緒中,可以有效的進行執行緒間互動,或者控制轉移。一個執行緒執行了wait, 需要其他執行緒notify,或者notifyAll, 才可以繼續執行。舉個例子,消費者和生產者,當沒有Message被生產者生產時,消費者則一直處於wait狀態,直到生產者生產了一條Message,然後notify消費者進行消費。

[注意點]

  1. 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.}  
  1. 執行wait()後會釋放鎖持有的鎖,其他等待同步中的執行緒這時會持有該鎖,並執行。

  2. wait()執行後,執行緒狀態會變為disabled, 想繼續執行除非以下事件中的一個發生:

    a)其他執行緒在此同步的引用上執行了notify()或者notifyAll(),注意是和wait相同引用上執行的notify()。

    b)執行緒被其他執行緒中斷,會報InterruptedException。

    c)wait可以指定timeout, 當timeout時間過去時。

  3. wait繼續執行需要重新獲得該引用的鎖,若有其他執行緒佔有著此鎖,則仍然無法恢復。

  4. notify是隨機喚醒一個wait中的執行緒,notifyAll是把所有wait中的執行緒全部喚醒。notify的物件仍舊是其持有的鎖的引用。

  5. 如果沒有執行緒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.}  

在你需要互斥的地方,就可以用這個類物件的方法了