1、上一篇说的流水号自动增长,存在两个问题,第一如果编号是字母+数字格式的,数字自增可以使用AtomicInteger实现,但是与字母组合拼接肯定是一个非原子、非线程安全的,可以通过线程同步实现;第二是如果服务集群部署,涉及到分布式锁问题。
下面的这个例子就是解决分布式环境下实现流水号自动增长的功能,通过线程同步+redis分布式锁实现。
代码实例如下:
@Servicepublic 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<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(); } }}
联系客服