1、上一篇說的流水號自動增長,存在兩個問題,第一如果編號是字母+數(shù)字格式的,數(shù)字自增可以使用AtomicInteger實(shí)現(xiàn),但是與字母組合拼接肯定是一個非原子、非線程安全的,可以通過線程同步實(shí)現(xiàn);第二是如果服務(wù)集群部署,涉及到分布式鎖問題。 下面的這個例子就是解決分布式環(huán)境下實(shí)現(xiàn)流水號自動增長的功能,通過線程同步+redis分布式鎖實(shí)現(xiàn)。 代碼實(shí)例如下: @Service
public class DistributedLock {
@Autowired
private IRedisDao redisDao;
@Autowired
private IUserDao userDao;
//用戶編碼當(dāng)前最大值,存儲在redis中的key
private static final String CURRENT_MAX_USER_CODE_KEY = "CURRENT_MAX_USER_CODE_KEY";
//用戶編碼前綴
private final static String PRE_GROUP_CODE = "w";
//用戶編碼初始值,格式:前綴+8000000開始的流水,如:w8000001
private static final String INIT_USER_CODE = PRE_GROUP_CODE+"8000000";
//分布式鎖的鎖定時長,單位秒
private static final int LOCK_TIME = 5;
//分布式鎖的key
private static final String LOCK_KEY = "USER_CODE_INC_LOCK";
//緩存初始化
@PostConstruct
public void initCurrentMaxUserCode(){
//初始化獲取數(shù)據(jù)庫中最大編碼值
String currentMaxUserCode = userDao.getMaxUserCode();
//如果為空,則設(shè)置為初始值
if(StringUtils.isBlank(currentMaxUserCode)){
currentMaxUserCode = INIT_USER_CODE;
}
redisDao.set(CURRENT_MAX_USER_CODE_KEY, currentMaxUserCode,0);
}
/**
* @Author javaloveiphone
* @Date 創(chuàng)建時間:2017年4月8日
* @Description :獲取最大編碼值,當(dāng)前服務(wù)被部署多套,采用:synchronized+redis分布式鎖 形式共同完成
* @param timeOut 循環(huán)獲取最大值超時時長
* @param timeUnit 超時單位
* @return
* String
*/
public synchronized String getNewMax(long timeOut,TimeUnit timeUnit){
String newMaxValue = null;
if(timeUnit == null){
timeUnit = TimeUnit.SECONDS;
}
long start = System.nanoTime();
do{
String lockValue = String.valueOf(new Date().getTime());
int lockFlag = redisDao.setnx(LOCK_KEY, lockValue).intValue();
//獲取鎖
if(lockFlag == 1){
//1、設(shè)置有效期,防止當(dāng)前鎖異?;虮罎?dǎo)致鎖釋放失敗
redisDao.expire(LOCK_KEY, LOCK_TIME);
//2、獲取當(dāng)前最大編碼值
String currentMaxValue = (String)redisDao.get(CURRENT_MAX_USER_CODE_KEY);
//如果redis中該值丟失,重新執(zhí)行初始化
if(StringUtils.isBlank(currentMaxValue)){
initCurrentMaxUserCode();
currentMaxValue = (String)redisDao.get(CURRENT_MAX_USER_CODE_KEY);
}
//3、將最大值加1,獲取新的最大值
int currentMaxNum = Integer.parseInt(currentMaxValue.substring(currentMaxValue.indexOf(PRE_GROUP_CODE)+1));
newMaxValue = PRE_GROUP_CODE + (currentMaxNum + 1);
//4、將新的最大值同步到redis緩存
redisDao.set(CURRENT_MAX_USER_CODE_KEY, newMaxValue,0);
//5、釋放鎖,redis執(zhí)行刪除方法
redisDao.remove(LOCK_KEY);
break;
//未獲取鎖
}else if(lockFlag == 0){
System.out.println(Thread.currentThread().getName()+"=====未獲取鎖,未超時將進(jìn)入循環(huán)");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果未超時,則循環(huán)獲取鎖
}while(System.nanoTime()-start<timeUnit.toNanos(timeOut));
return newMaxValue;
}
public void getMaxUserCode(){
for(int i=0;i<10;i++){
Thread t = new Thread(){
@Override
public void run() {
System.out.println(getNewMax(5,TimeUnit.SECONDS));
}
};
t.setName("線程"+i);
t.start();
}
}
}
|