自制Java中的Mutex類


同步問題中,一個很重要的問題是同步的域,什么是同步的域呢?簡單以 synchronized 這個關鍵字來說,就是它所同步的范圍。並發編程中很多時候出現的問題沒有選好同步范圍所導致的。但現有的同步關鍵字synchronized所能體現出來的對域的控制,估計用過的的人都應該感覺到並不是很理想。這個時候是不是很懷念Windows下所提供的Mutex操作,通過申請和釋放的函數的位置控制同步的域,用起來要方便一些。

現在為了方便我們自己也可以做一個類似Windows的Mutex的類來方便我們對域的控制,下面看這個類的制作過程。

和制作上一個類一樣,我們先來說一下希望這個類完成的功能:

1.調用getMutexFlag(),僅能是並發的線程中的一個繼續執行,其余的阻塞。

2.調用freeMutexFlag(),執行的線程離開同步塊(臨界區),並能從阻塞線程中釋放一個線程從getMutexFlag()中返回。

3.getMutexFlag()、freeMutexFlag(),都必須是原子操作。

上面這三個條件其實就是對Windows下Mutex的要求。

一開始我們也許會寫出這樣的代碼:

 1 package com.choi;
 2 
 3 public class MutexFlag {
 4 
 5     protected Thread currentThread = null;
 6 
 7     public synchronized void getMutexFalg() {
 8         if (currentThread == null) {
 9             currentThread = Thread.currentThread();
10         }
11         while (currentThread != Thread.currentThread()) {
12             try {
13                 wait();
14                 if (currentThread == null) {
15                     currentThread = Thread.currentThread();
16                 }
17             } catch (Exception e) {
18             }
19         }
20     }
21     
22     public synchronized void freeMutexFlag(){
23         if(currentThread!=null){
24             currentThread = null;
25             try {
26                 notify();
27             } catch (Exception e) {
28             }
29         }
30     }
31 }

這段代碼看起來是沒有問題的,其實這段代碼運行起來確實是沒有問題的,但我們能把它提供給我們的項目組用嗎?答案是不能的,我們來考慮一種情況:

項目組有三個程序員A,B,C。 A用這個類寫了一個函數funA(get到Mutex未釋放),B用這個類寫了一個函數funB(get到Mutex未釋放),C調用了funA,funB,用在一個線程中了。則這個線程運行到funA時得到了Mutex,運行到funB時再去申請Mutex能申請到嗎?通過分析代碼執行,肯定是NO。並申請不到,因為第一個還沒有釋放。Java里面如果是兩個synchronized嵌套,則沒有問題,第一個得到鎖后,第二個如申請的是同一個鎖,就會自動判斷為已申請到。

我們可以用一個變量來是我們的MutexFlag實現synchronized的嵌套解決方案:

 1 package com.choi;
 2 
 3 public class MutexFlag {
 4 
 5     protected Thread currentThread = null;
 6     protected int count = 0;
 7 
 8     public synchronized void getMutexFalg() {
 9         while (tryGetMutexFlag() == false) {
10             try {
11                 wait();
12             } catch (InterruptedException e) {
13                 e.printStackTrace();
14             }
15         }
16     }
17 
18     public synchronized boolean tryGetMutexFlag() {
19         if (currentThread == null) {
20             currentThread = Thread.currentThread();
21             count = 1;
22             return true;
23         }
24         if (currentThread == Thread.currentThread()) {
25             count++;
26             return true;
27         }
28         return false;
29     }
30 
31     public synchronized void freeMutexFlag() {
32         if (currentThread == Thread.currentThread()) {
33             count--;
34         }
35         if (count == 0) {
36             currentThread = null;
37             notify();
38         }
39     }
40 }

為了編寫方便我把判斷獲取MutexFlag的代碼寫到了同步的tryGetMutexFlag中了,沒申請成功一次就對count做加一操作,每一free都對count做減一操作,那么當count從新等於零的時候我們就去釋放一個線程獲取MutexFlag。

哦,有人說如果沒有調用get就調用free會有問題,這里說明一下沒有問題的,因為如果沒有get,currentThread是等於null的,所以count==0,然后就會調用notify,其實沒有問題啦,測試發現,notify不會拋出人異常,相當於空操作。

歡迎到我的GitHub主頁獲取代碼:https://github.com/choitony/Java-MutexFlag-Class/tree/master


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM