java 分布式自增主键_java项目实现流水号自动增长(分布式环境)

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

}

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java实现分布式自增 ID 可以使用 Snowflake 算法,该算法使用一个 64 位的二进制数字,其中前 41 位为时间戳,接着是 10 位的机器 ID 和 12 位的序列号。 以下是一个简单的 Java 实现: ```java public class SnowflakeIdGenerator { // 开始时间戳(2021-01-01) private final static long START_TIMESTAMP = 1609430400000L; // 机器 ID 所占位数 private final static long MACHINE_BITS = 10L; // 序列号所占位数 private final static long SEQUENCE_BITS = 12L; // 最大机器 ID private final static long MAX_MACHINE_ID = ~(-1L << MACHINE_BITS); // 最大序列号 private final static long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS); // 机器 ID 左移位数 private final static long MACHINE_SHIFT = SEQUENCE_BITS; // 时间戳左移位数 private final static long TIMESTAMP_SHIFT = MACHINE_BITS + SEQUENCE_BITS; // 当前机器 ID private long machineId; // 当前序列号 private long sequence = 0L; // 上次生成 ID 的时间戳 private long lastTimestamp = -1L; public SnowflakeIdGenerator(long machineId) { if (machineId > MAX_MACHINE_ID || machineId < 0) { throw new IllegalArgumentException(String.format("Machine ID can't be greater than %d or less than 0", MAX_MACHINE_ID)); } this.machineId = machineId; } public synchronized long nextId() { long timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } if (timestamp == lastTimestamp) { sequence = (sequence + 1) & MAX_SEQUENCE; if (sequence == 0) { timestamp = nextTimestamp(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT) | (machineId << MACHINE_SHIFT) | sequence; } private long nextTimestamp(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } } ``` 使用示例: ```java SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1); long id = idGenerator.nextId(); ``` 该实现中,机器 ID 和序列号都是自增的,时间戳取当前时间,如果当前时间小于上一次生成 ID 的时间戳,则抛出异常。如果当前时间等于上一次生成 ID 的时间戳,则序列号自增,如果序列号超过最大值,则等待下一个时间戳。如果当前时间大于上一次生成 ID 的时间戳,则序列号重置为 0。最后,将时间戳、机器 ID 和序列号拼接成一个 64 位的二进制数字作为 ID 返回。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值