一. 缓存方式
以下是同个用户同时提交多个问题时的防并发处理
<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>