java项目实现流水号自动增长-2-分布式环境

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()-startreturn 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

你可能感兴趣的:(j2se,Spring)