標籤:
同步問題中,一個很重要的問題是同步的域,什麼是同步的域呢?簡單以 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用這個類寫了一個函數(get到Mutex未釋放),B用這個類寫了一個函數(get到Mutex未釋放),C調用了A和B的函數,用在一個線程中了。則這個線程運行到A時得到了Mutex,運行到B時再去申請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
自製Java中的Mutex類