1. 程式人生 > 其它 >java資源競爭問題,理解Semaphore及其用法詳解

java資源競爭問題,理解Semaphore及其用法詳解

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();
}