1、上一篇说的流水号自动增长,存在两个问题,第一如果编号是字母+数字格式的,数字自增可以使用AtomicInteger实现,但是与字母组合拼接肯定是一个非原子、非线程安全的,可以通过线程同步实现;第二是如果服务集群部署,涉及到分布式锁问题。
下面的这个例子就是解决分布式环境下实现流水号自动增长的功能,通过线程同步+redis分布式锁实现。
代码实例如下:
@Service
public class DistributedLock {
@Autowired
private IRedisDao redisDao;
@Autowired
private IUserDao userDao;
//用户编码当前最大值,存储在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(){
//初始化获取数据库中最大编码值
String currentMaxUserCode = userDao.getMaxUserCode();
//如果为空,则设置为初始值
if(StringUtils.isBlank(currentMaxUserCode)){
currentMaxUserCode = INIT_USER_CODE;
}
redisDao.set(CURRENT_MAX_USER_CODE_KEY, currentMaxUserCode,0);
}
/**
* @Author javaloveiphone
* @Date 创建时间:2017年4月8日
* @Description :获取最大编码值,当前服务被部署多套,采用:synchronized+redis分布式锁 形式共同完成
* @param timeOut 循环获取最大值超时时长
* @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、设置有效期,防止当前锁异常或崩溃导致锁释放失败
redisDao.expire(LOCK_KEY, LOCK_TIME);
//2、获取当前最大编码值
String currentMaxValue = (String)redisDao.get(CURRENT_MAX_USER_CODE_KEY);
//如果redis中该值丢失,重新执行初始化
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执行删除方法
redisDao.remove(LOCK_KEY);
break;
//未获取锁
}else if(lockFlag == 0){
System.out.println(Thread.currentThread().getName()+"=====未获取锁,未超时将进入循环");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果未超时,则循环获取锁
}while(System.nanoTime()-start
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();
}
}
}
参考:http://blog.csdn.net/x_i_y_u_e/article/details/50864205
http://blog.csdn.net/java2000_wl/article/details/8740911