java版雪花算法

一、雪花算法图

在这里插入图片描述

二、java 雪花算法代码

1.雪花算法,分为三块,分别是时间,机器码,序列化

public class SnowFlakeUtil {

  / **
 * 开始时间
 */
private final long START_TIMESTAMP = 1577808000000L; //2020-01-01
/**
 * 机器位移数据
 */
private final static long WORK_ID_BIT = 10L;

/**
 * 机器标识最大值,最多1024台
 */
private final static long WORK_ID_MAX = 1L << WORK_ID_BIT;

/**
 * 序列化唯一数据
 */
private final static long SEQUENCE_BIT = 12L;


/**
 * 序列化最大值,4095个数据
 */
private final static long SEQUENCE_MAX = 1L<<SEQUENCE_BIT;

/**
 * 时间位移数据
 */
private final static long TIME_BIT = WORK_ID_BIT+SEQUENCE_BIT;

/**
 * 上一毫秒时间戳
 */
private long BEFORE_TIME = 0L;

/**
 * 序列化初始值
 */
private  long SEQUENCE_START = 0L;


 public SnowFlakeUtil(){
}

/**
 * 获取当前最新的时间轴
 * @return
 */
private long nowTime(){
    return System.currentTimeMillis();
}

/**
 * 等待下一毫米
 * @return
 */
private long getNextMill(){
    long nextTime = nowTime();
    while (nextTime <= BEFORE_TIME) {
        nextTime = nowTime();
    }
    return nextTime;
}
/**
 * 分布式id,线程安全
 * @return
 */
public synchronized long nextId(long workId){
    if(workId>WORK_ID_MAX){
        throw new RuntimeException("机器码超过指定的最大机器码数据");
    }
    long time = nowTime();
    //当前时间戳小于上一秒时间戳,就意味着时间回拨,导致的
    if(BEFORE_TIME>0&&time<BEFORE_TIME){
        throw new RuntimeException("时间回拨");
    }
    //如果时间相同,就判断上一个序列化值
    if(time==BEFORE_TIME){
        SEQUENCE_START = SEQUENCE_START+1;
        //大于的话,就意味着这一毫秒,已经没有序列值,就一直等待到下一毫秒
        if(SEQUENCE_START>SEQUENCE_MAX){
            time = this.getNextMill();
            SEQUENCE_START = 0L;
        }
    }else{
        //时间不同,就意味着开始
        SEQUENCE_START = 0L;
    }
    //初始最新时间
    BEFORE_TIME = time;
    return (time - START_TIMESTAMP) << TIME_BIT //时间戳部分
            | workId << SEQUENCE_BIT       //j机器码部分
            | SEQUENCE_START;  //j序列化部分

}

public static void main(String arg[]){

    SnowFlakeUtil snowFlakeUtil = new SnowFlakeUtil();
    for(int i=0;i<2000;i++) {
        System.out.println(snowFlakeUtil.nextId(1L));
    }
}

三、雪花算法缺陷

1:由于服务器会进行时间校正,就会出现服务器时间回拨,这时,雪花算法就会出现一段时间生产不了,影响服务使用。
2:由于雪花long类型是64bit,前端用long会失精度,前端要用字符串类型
3:百度的UidGenerator分布式ID解决了时间回拨问题,可以用它

您好,以下是 Java 雪花算法实现: ``` public class Snowflake { // 机器ID占用的位数 private static final long MACHINE_BIT = 5L; // 数据中心ID占用的位数 private static final long DATA_CENTER_BIT = 5L; // 序列号占用的位数 private static final long SEQUENCE_BIT = 12L; // 最大的机器ID和数据中心ID private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_BIT); private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_BIT); private static final long EPOCH = 1483200000000L; private long machineId; private long dataCenterId; private long sequence = 0L; private long lastTimestamp = -1L; public Snowflake(long machineId, long dataCenterId) { if (machineId > MAX_MACHINE_ID || machineId < 0) { throw new IllegalArgumentException("Machine ID can't be greater than " + MAX_MACHINE_ID + " or less than 0!"); } if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) { throw new IllegalArgumentException("Datacenter ID can't be greater than " + MAX_DATA_CENTER_ID + " or less than 0!"); } this.machineId = machineId; this.dataCenterId = dataCenterId; } public synchronized long nextId() { long currentTimestamp = System.currentTimeMillis(); if (currentTimestamp < lastTimestamp) { throw new RuntimeException("Clock moved backwards. Refusing to generate id."); } if (currentTimestamp == lastTimestamp) { sequence = (sequence + 1) & ~(-1L << SEQUENCE_BIT); if (sequence == 0L) { currentTimestamp = tilNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = currentTimestamp; return (currentTimestamp - EPOCH) << (MACHINE_BIT + DATA_CENTER_BIT + SEQUENCE_BIT) | dataCenterId << (MACHINE_BIT + SEQUENCE_BIT) | machineId << SEQUENCE_BIT | sequence; } private long tilNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } } ``` 希望能对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值