java資源競爭問題,理解Semaphore及其用法詳解
阿新 • • 發佈:2021-12-09
Semaphore也是一個執行緒同步的輔助類,可以維護當前訪問自身的執行緒個數,並提供了同步機制。使用Semaphore可以控制同時訪問資源的執行緒個數,例如,實現一個檔案允許的併發訪問數。
Semaphore的主要方法摘要:
void acquire():從此訊號量獲取一個許可,在提供一個許可前一直將執行緒阻塞,否則執行緒被中斷。
void release():釋放一個許可,將其返回給訊號量。
int availablePermits():返回此訊號量中當前可用的許可數。
boolean hasQueuedThreads():查詢是否有執行緒正在等待獲取。
程式碼示例:
/* * 防止同一臺伺服器上同時有多個人預約同一資源的同一日期 * 形象解釋: Semaphore機制類似一個服務大廳(最多容納20人),這個服務大廳裡面有多個服務視窗(每一天的資源場地):A、 * B、C,這個服務大廳最多能進來20個人,進來的人都需要在各自需要辦理的服務視窗排隊等待,只有等到上一個人離 * 開(釋放訊號)下一個人才能進入該服務視窗辦理,但是各服務視窗之間是並行的互不影響。 */ //用於控制訪問量的訊號變數,一次最多進入20個請求,按照先進先出許可 private static Semaphore bookLockCounter = new Semaphore(20,true); try{ //同一個應用服務內只允許20個人同時進入資源預定排隊,否則報系統忙,請稍候再試。 if(bookLockCounter.getQueueLength() >= 20 || bookLockCounter.tryAcquire(500,TimeUnit.MILLISECONDS) == false) { throw new TzException("System is busy, please try again later."); } Semaphore tmpSemaphore = null; boolean hasPlanSemaphore = false; //記錄成功獲取訊號,預定完成後需要釋放 try { //獲取當資源日期對應的訊號 //用資源ID+日期來標識同一資源的同一日期,其他人在想預約這一資源就需要排隊,但是預約其他資源的話就不用排隊 Map.Entry<String,Semaphore> tmpSemaphoreObject = tzGDObject.getSemaphore(tzms_class_addr_defn_tid + "-" +date); if(tmpSemaphoreObject == null || tmpSemaphoreObject.getKey() == null || tmpSemaphoreObject.getValue() == null) { //如果返回的訊號燈為空,報系統忙,請稍後再試(一般不會) throw new TzException("System is busy, please try again later."); }else{ tmpSemaphore = tmpSemaphoreObject.getValue(); //通過獲取的訊號將每個資源日期並行執行,資源日期內序列執行 tmpSemaphore.acquire(); //獲取訊號燈標記,預定完成後需要釋放 hasPlanSemaphore = true; } /*//////////////////////////////////////////////////////////////////////////////*/ /*/////////////////////此處開始執行預定的邏輯/////////////////////////////////////*/ /*//////////////////////////////////////////////////////////////////////////////*/ } catch(TzException e) { e.printStackTrace(); //預定失敗,失敗資訊獲取:e.getMessage() } finally { //釋放資源訊號量 if(hasPlanSemaphore){ tmpSemaphore.release(); } //釋放訊號量 bookLockCounter.release(); } } catch(TzException e) { e.printStackTrace(); }
其中tzGDObject類中:
// 用於將同一個服務上的並行訪問序列化的訊號燈對映變數 private static HashMap<String, Semaphore> semaphoreMap = new HashMap<String, Semaphore>(); // 用於將用於將同一個服務上的並行訪問序列化的訊號燈變數 private static Semaphore sequenceSemaphore = new Semaphore(1, true); /** * 獲取呼叫者資訊的方法 */ private String getCallerName() { String callerName = ""; StackTraceElement tmpstack[] = Thread.currentThread().getStackTrace(); boolean findFlag = false; for (StackTraceElement stackitem : tmpstack) { if ((stackitem.getClassName().indexOf(getClass().getName())) >= 0) { findFlag = true; } else if (findFlag == true) { callerName = stackitem.getClassName() + "." + stackitem.getMethodName(); break; } } return callerName; } /** * 獲取訊號燈變數的方法 */ public Map.Entry<String, Semaphore> getSemaphore(String semaphoreName) { Semaphore tmpSemaphore = null; String tmpSemaphoreName = getCallerName() + "-" + semaphoreName; System.out.println("successfully get the traffic light variable name[Thread ID : " + Thread.currentThread().getId() + "]: " + tmpSemaphoreName); if (semaphoreMap.containsKey(tmpSemaphoreName) == true) { tmpSemaphore = semaphoreMap.get(tmpSemaphoreName); } else { try { // 獲取訊號燈,同步執行緒 if (sequenceSemaphore.tryAcquire(100, TimeUnit.MILLISECONDS) == false) { return null; } } catch (Exception e) { return null; } // 再次判斷一下指定的訊號燈是否已建立 if (semaphoreMap.containsKey(tmpSemaphoreName) == true) { tmpSemaphore = semaphoreMap.get(tmpSemaphoreName); } else { tmpSemaphore = new Semaphore(1, true); semaphoreMap.put(tmpSemaphoreName, tmpSemaphore); } // 釋放訊號燈 sequenceSemaphore.release(); } HashMap<String, Semaphore> tmpHashMap = new HashMap<String, Semaphore>(); tmpHashMap.put(tmpSemaphoreName, tmpSemaphore); return tmpHashMap.entrySet().iterator().next(); } /** * 獲取訊號燈變數的方法,用於在兩個不同的地方對同一個訊號燈變數的控制,且呼叫該方法的引數相同 * @author zhanglang * @param semaphoreKey * @param semaphoreName * @return */ public Map.Entry<String, Semaphore> getSemaphore(String semaphoreKey, String semaphoreName) { Semaphore tmpSemaphore = null; String tmpSemaphoreName = "MULTI_" + semaphoreKey + "-" + semaphoreName; if (semaphoreMap.containsKey(tmpSemaphoreName) == true) { tmpSemaphore = semaphoreMap.get(tmpSemaphoreName); } else { try { // 獲取訊號燈,同步執行緒 if (sequenceSemaphore.tryAcquire(100, TimeUnit.MILLISECONDS) == false) { return null; } } catch (Exception e) { return null; } // 再次判斷一下指定的訊號燈是否已建立 if (semaphoreMap.containsKey(tmpSemaphoreName) == true) { tmpSemaphore = semaphoreMap.get(tmpSemaphoreName); } else { tmpSemaphore = new Semaphore(1, true); semaphoreMap.put(tmpSemaphoreName, tmpSemaphore); } // 釋放訊號燈 sequenceSemaphore.release(); } HashMap<String, Semaphore> tmpHashMap = new HashMap<String, Semaphore>(); tmpHashMap.put(tmpSemaphoreName, tmpSemaphore); return tmpHashMap.entrySet().iterator().next(); }