手写雪花算法

/**
 * 雪花id的生成器
 *
 */
public class IdGenerator {

//单机版本的线程安全的id发号器,一旦变成集群状态就不行了
//    private static LongAdder longAdder = new LongAdder();   //这玩意儿就是利用的cas
//
//    public static long getId(){
//        longAdder.increment();
//        return longAdder.sum();
//    }


    // 雪花算法 -- 世界上没有一片雪花是一个样的
    // 机房号(数据中心)5bit  32
    // 机器号         5bit  32
    //时间戳(long 表示1970年1月1日到现在的毫秒数)  原本64位表示的时间,必须减少至32,自由选择一个比较近的时间
    //比如我们公司成立的日期作为起始时间的时间点
    // 同一个机房的同一个机器的同一个时间可以因为并发量很大需要多个id
    //序列号 12bit  5+5+42+12 = 64

    //起始时间戳
    public static final long START_STAMP = DateUtil.get("2022-1-1").getTime();
    //数据中心
    public static final long DATA_CENTER_BIT = 5;
    //机器数
    public static final long MACHINE_BIT = 5;
    //序列号
    public static final long SEQUENCE_BIT = 12;

    //最大值 Math.pow(2,5)-1
    public static final long DATA_CENTER_MAX = ~(-1l << DATA_CENTER_BIT);
    public static final long MACHINE_MAX = ~(-1l << MACHINE_BIT);
    public static final long SEQUENCE_MAX = ~(-1l << SEQUENCE_BIT);

    //将这些数据拼接到一个long类型中 采用二进制的或运算
    //时间戳 (42)                                 机房号(5)  机器号(5)  序列号(12)
    //101010101010101010101010101010101010101012  10101       10101     101010101010
    public static final long TIMESTAMP_LEFT = DATA_CENTER_BIT + MACHINE_BIT + SEQUENCE_BIT;
    public static final long DATA_CENTER_LEFT = MACHINE_BIT + SEQUENCE_BIT;
    public static final long MACHINE_LEFT = SEQUENCE_BIT;


    private long dataCenterId;
    private long machineId;
    private LongAdder sequenceId = new LongAdder();
    //时钟回拨的问题,我们需要去处理
    private long lastTimeStamp = -1L;

    public IdGenerator(long dataCenterId, long machineId) {
        //判断传入的参数是否合法
        if (dataCenterId > DATA_CENTER_MAX || machineId > MACHINE_MAX) {
            throw new IllegalArgumentException("您传入的数据中心编号或机器号不合法");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    public long getId() {
        //第一步:处理时间戳的问题
        long currentTime = System.currentTimeMillis();

        long timeStamp = currentTime - START_STAMP;//当前时间戳减去定义的起始时间戳

         //判断时钟回拨
        if(timeStamp < lastTimeStamp){
            throw new RuntimeException("您的服务器进行了时钟回调.");
        }


        //sequenceId需要做一些处理,如果是同一个时间节点,必须自增
        if (timeStamp == lastTimeStamp) {//如果时间等于上一次的时间说明这两次请求在同一个时间点并发,让序列号自增可以保证雪花id不一样
            sequenceId.increment();

            if(sequenceId.sum() >= SEQUENCE_MAX){
                   timeStamp = getNextTimeStamp();
                   sequenceId.reset();
            }
        } else {//如果时间戳不一致那就没必要做操作了,反正时间戳不一样,别的随便是多少都可以
            sequenceId.reset();
        }
        //执行结束将时间戳赋值给lastTimeStamp
        lastTimeStamp = timeStamp;
        long sequence = sequenceId.sum();
        return timeStamp << TIMESTAMP_LEFT | dataCenterId << DATA_CENTER_LEFT
                | machineId << MACHINE_LEFT | sequence;
    }

    /**
     * 获得下一个时间戳的方法
     * @return
     */
    private long getNextTimeStamp() {
        //获取当前的时间戳
        long current = System.currentTimeMillis() - START_STAMP;
        //如果一样就一直循环,直到下一个时间戳
        while (current == lastTimeStamp){
            current = System.currentTimeMillis() - START_STAMP;
        }
        return current;
    }




}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谢少迪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值