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();
}
posted @ 2021-12-09 19:44  CheungLang  阅读(602)  评论(0)    收藏  举报