一. 緩存方式
以下是同個用戶同時提交多個問題時的防併發處理
<span style="font-size:18px;">String simultaneouslyKey = "_key_simultaneously_"+loginAccount.getAccountId();
Object obj = cacheClient.get(simultaneouslyKey);
if (obj == null) {
cacheClient.set(simultaneouslyKey, 1, new Date(System.currentTimeMillis() + 5000));
} else {
if(1 == (Integer)obj){
json.put("ret", -1);
json.put("msg", "您提交太頻繁,請稍後提交");
out.println(json.toString());
return ;
}
}</span>
緩存方式在分佈式環境下依然會出現併發的問題,下面介紹同步鎖的方式,在分佈式環境下同樣有效
二. 同步鎖方式
以下是同步鎖的兩個工具類:
<span style="font-size:18px;">public class McSyncUtils {
/**
* 使用mc的併發CAS模式執行一段在分佈式環境中需要同步的代碼
* @param mcc
* @param mutexKey 同步鎖的key
* @param event 同步處理事件
* @param mustExecSuccess 是否每個線程都必須等待執行success()方法,如果爲false則表示第一次獲取失敗之後再也不等待繼續執行了
* @throws InterruptedException
* void
* @author
* @update date
*/
public static void execSyncCode(MemCachedClient mcc, String mutexKey,
SyncEvent event, boolean mustExecSuccess) throws InterruptedException{
String currentValue = System.currentTimeMillis() + "";
// 添加互斥鎖,最長鎖10秒鐘
if(mcc.add(mutexKey, currentValue, new java.util.Date(10 * 1000))){
String val = mcc.get(mutexKey).toString();
if(currentValue.equals(val)){
if(event.syncCondition()){
event.success();
} else {
event.failure();
}
// 取消互斥鎖
mcc.delete(mutexKey);
return;
}
}
// 該互斥鎖已經被其他線程佔用
if(mustExecSuccess){
// 等待獲取鎖資源
Thread.sleep(50);
// 重複執行
execSyncCode(mcc, mutexKey, event, mustExecSuccess);
} else {
event.failure();
}
}
}</span>
<span style="font-size:18px;">/**
*
* 同步處理事件,獲得執行機會後執行sucess()方法,獲取失敗則執行failure()方法
* @author
* @create date
*/
public interface SyncEvent {
/**
* 執行同步業務代碼的條件,滿足該條件的時候纔會進行獲取鎖執行success()中的代碼
*/
boolean syncCondition();
/**
* 獲取到同步鎖之後執行的事件
*/
void success();
/***
* 獲取同步鎖失敗或者沒進行獲取同步鎖的時候執行的事件
*/
void failure();
}</span>
案例:
<span style="font-size:18px;"> try {
final MemCachedClient mcc = env.getBean("mcc", MemCachedClient.class);
final String key = "exchangeGift2_" + giftId + "_" + accountId + "_" + mag;
final String mutexKey = "exchangeGift2_" + giftId + "_" + accountId + "_" + mag + "_mutexkey";
McSyncUtils.execSyncCode(mcc, mutexKey, new SyncEvent(){
@Override
public boolean syncCondition(){
// 如果該投票對應的key不存在則獲取鎖進行執行
return mcc.get(key) == null;
}
@Override
public void success(){
mcc.set(key, "1", new java.util.Date(2 * 1000L));//2秒時間內對一個用戶只能領取一次
}
@Override
public void failure(){
throw new CoinException(111, "請稍後再來申請");
}
}, false);
} catch (InterruptedException e1) {
throw new CoinException(111, "請稍後再來申請");
}</span>