package com.iwhalecloud.basetool.asyncimpexpbase.common;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.security.SecureRandom;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
/**
* KeyGenerator 主键生成工具
*
* @author huang.wu@iwhalecloud.com
* @date 2021/7/11
*/
public class KeyGenerator {
public static final int INCREMENR_LIMIT = 89999;
public static final int INCREMENT_INITIA = 10000;
public static final int DATE_MILL_FACTOR = 1000;
private KeyGenerator() {
}
/**
* 时间戳格式
*/
private static final String DATE_FORMAT = "yyyyMMddHHmmss";
/**
* 随机数位数
*/
private static final int RANDOM_BOUND = 9000;
/**
* 时间填充位数
*/
private static final int SEQ_TIME_PAD_SIZE = 4;
/**
* 随机数填充位数
*/
private static final int RANDOM_PAD_SIZE = 1000;
/**
* 填充数
*/
private static final String PAD_CHAR = "0";
/**
* 防止重复的自增
*/
private static AtomicInteger increment = new AtomicInteger(INCREMENT_INITIA);
private static SecureRandom random = new SecureRandom();
/**
* 表主键生成
*
* @return
*/
public static String generateId() {
Date now = new Date();
String dateStr = DateFormatUtils.format(now, DATE_FORMAT);
String seqFromTime = StringUtils.leftPad(Long.toString(now.getTime() % DATE_MILL_FACTOR), SEQ_TIME_PAD_SIZE, PAD_CHAR);
String randomStr = String.valueOf(random.nextInt(RANDOM_BOUND) + RANDOM_PAD_SIZE);
int inc = increment.getAndIncrement();
//因为无锁,如果有多个线程同时执行这个方法调用,最大可能会被加到90099,也就是说以上算法如果有超过10000个线程并发调用会有问题
//此情况下可以考虑扩大自增范围
//同时如果并发太高,1ms内创建的订单数超过79998,即qps超过79999万时也有一亿分之一的概率重复
if (increment.get() >= INCREMENR_LIMIT) {
increment.set(INCREMENT_INITIA);
}
return dateStr + seqFromTime + inc + randomStr;
}
/**
* 表主键生成,可指定前缀
*
* @return
*/
public static String generateId(String prefix) {
Date now = new Date();
String dateStr = DateFormatUtils.format(now, DATE_FORMAT);
String seqFromTime = StringUtils.leftPad(Long.toString(now.getTime() % DATE_MILL_FACTOR), SEQ_TIME_PAD_SIZE, PAD_CHAR);
String randomStr = String.valueOf(random.nextInt(RANDOM_BOUND) + RANDOM_PAD_SIZE);
int inc = increment.getAndIncrement();
//因为无锁,如果有100个线程同时执行这个方法调用,最大可能会被加到90099,也就是说以上算法如果有超过10000个线程并发调用会有问题
//此情况下可以考虑扩大自增范围
//同时如果并发太高,1ms内创建的订单数超过79998,即qps超过7999万时也有一亿分之一的概率重复
if (increment.get() >= INCREMENR_LIMIT) {
increment.set(INCREMENT_INITIA);
}
return (prefix != null && prefix.length() > 0 ? prefix : "") + dateStr + seqFromTime + inc + randomStr;
}
public static void main(String[] args) {
System.out.println(generateId());
System.out.println(generateId().length());
System.out.println(generateId("110"));
System.out.println(generateId("110").length());
}
}