有这样一个业务场景,需要根据某个单据号生成5位数自增且不重复id,第一时间想到了使用AtomicInteger
来实现,它提供了线程安全的整数操作,它通过利用底层硬件的原子性指令,能够在多线程环境中高效地实现整数的无锁更新,避免了传统同步机制带来的性能开销,在高并发场景下成为计数器可大幅提高程序的执行效率,同时又保证了数据一致性。
代码如下:
private static final int ID_LENGTH = 5;//定义生成ID的长度
private static final AtomicInteger nextId = new AtomicInteger(10000);
public static String generateId() {
int currentId;
do {
currentId = nextId.getAndIncrement();
if (currentId >= 100000) {
nextId.set(10000); // 循环使用ID空间
}
// 确保ID是5位数,不足前面补0
} while (!isValidId(String.format("%05d", currentId)));
return String.format("%05d", currentId);
}
private static boolean isValidId(String id) {
return true;
}
后面经过实践,发现不能实现我的业务诉求,他只能一直自增生成,越到后面数据量越大,可能数据就会超过5位数,开始思考换一种方式,加入一个key值,遇到不同的key重新从10000开始生成
代码如下:
private static final int ID_LENGTH = 5;
private static final String PREFIX = "ID:";
private static final Long ID_EXPIRE = 72 * 60 * 60L;
private static final AtomicInteger nextId = new AtomicInteger(10000);
public static String generateId(String decree) {
int currentId;
do {
currentId = nextId.getAndIncrement();
if (currentId >= 100000) {
nextId.set(10000); // 循环使用ID空间
}
// 确保ID是5位数,不足前面补0
} while (!isValidId(String.format("%05d", currentId),decree));
return String.format("%05d", currentId);
}
private static boolean isValidId(String id,String decree) {
//获取redis缓存的List
List<String> list = CacheUtil.getList(PREFIX+decree);
//判断当前id是否在list中,在就直接返回false,不在就把当前id存入List并返回true
if(list.contains(id)){
return false;
}
CacheUtil.setList(PREFIX+decree ,id ,ID_EXPIRE);
return true;
}
这样既能够保证数据不会超出5位数的限制又能保证不重复,但是后面使用过程中发现该接口的性能又不太理想,该业务场景下会有大量数据同时来生成ID,每个都要调用该方法,造成Redis访问次数增多,遂放弃使用Redis来存储
最后采用把它存放在本地ConcurrentHashMap
中,效率果然大大增加
代码如下:
private static final ConcurrentHashMap<String, AtomicInteger> lastGeneratedNumbers = new ConcurrentHashMap<>();
/**
* 生成一个五位数的自增数字,对于同一个 key,生成的数字不会重复。
*
* @param key 参数,用于区分不同的序列
* @return 五位数的自增数字
*/
public static synchronized int generateId(String key) {
// 获取或创建一个 AtomicInteger 对象
AtomicInteger counter = lastGeneratedNumbers.computeIfAbsent(key, k -> new AtomicInteger(10000));
// 自增并获取新的值
int value = counter.incrementAndGet();
// 检查是否超出五位数的范围
if (value > 99999) {
throw new IllegalStateException("The maximum number of unique five-digit numbers has been reached for key: " + key);
}
return value;
}
最后不知道各位是否有更好的方法来实现,欢迎留言