java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java根据时间戳生成有序ID

java如何根据时间戳生成有序ID

作者:紫金丨小飞侠

这篇文章主要介绍了java如何根据时间戳生成有序ID问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

引言

我们常用的主键有这么几种

1. 数据库自增主键,比如mysql的autoincrement,这种插入快,但是识别度不高

2. uuid 这个号称是全球唯一的,但是无序,没有实际意义,只能保证唯一

3. 时间戳,这种在分布式的场景下就需要考虑更多种情况

4. 雪花算法 snow flake ,分布式全局唯一主键,很牛,但是我觉得用起来也挺麻烦哈哈哈

所以在并发情况没那么大的时候用一个工具类搞定,我就是这么懒

工具类

@Slf4j
public class NumUtil {

    private static long tmpID = 0;
    private static final long LOCK_TIME = 1;
    private static final long INCREASE_STEP = 1;
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyMMddHHmmssSSS");
    private static final Lock LOCK = new ReentrantLock();


    public static long nextPkId() throws InterruptedException {
        //当前:(年、月、日、时、分、秒、毫秒)
        long timeCount;
        if (LOCK.tryLock(LOCK_TIME, TimeUnit.SECONDS)) {
            timeCount = Long.parseLong(sdf.format(new Date()));
            try {
                if (tmpID < timeCount) {
                    tmpID = timeCount;
                } else {
                    tmpID += INCREASE_STEP;
                    timeCount = tmpID;
                }
                return timeCount;
            } finally {
                LOCK.unlock();
            }
        } else {
            log.error("lock failed");
            return nextPkId();

        }
    }
}

贴上代码,这里用了当前时间,精确到毫秒级,如果有需要的话可以在实例化timeCount的时候乘以10或者100 1000之类的,这个看大家,然后加上锁,防止线程不安全的情况,加锁失败的时候递归,再来一次。

也可以使用synchronized做成同步方法,当中的区别下次再讨论。

有评论说宕机会导致tmpID归0导致已经使用过超出当前时间的ID,所以持久化这个tmpID也是可以的。

但这也就是在并发没那么高的情况下才使用这种方法,一般并发场景下还是分布式锁+推特的雪花算法解决。

测试

public static void numTest() {
        ExecutorService executorService = Executors.newCachedThreadPool();
        int n = 10000;
        List<Long> list = new ArrayList<>();
        CountDownLatch latch = new CountDownLatch(n);
        for (int i = 0; i < n; i++) {
            executorService.execute(() -> {
                //执行业务请求
                try {
                    list.add(NumUtil.nextPkId());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                latch.countDown();
            });
        }
        try {
            // 一定记得加上timeout时间,防止阻塞主线程
            latch.await(3000, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            log.error(e.getMessage());
        }
        //4.等待所有子任务完成,组装内容
        while (list.size() < n) {
            log.info("集合长度 >>> {}",list.size());
        }
        //5.关闭线程池
        executorService.shutdown();

        for (Long aLong : list) {
            System.out.println(aLong);
        }
    }

然后噼里啪啦打印了一万个ID,没有重复的,一秒以内生成

结论

当然这种只是为了单体或者是并发没有高到那么离谱的场景下使用,效率我觉得还不错,分布式的场景下可能需要用到redis的自增来计数以达到数据安全的效果

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家

您可能感兴趣的文章:
阅读全文