Twitter-Snowflake主键算法Java实现

这一篇理论的东西就不解释了,直接上代码,有兴趣的可以看简书上另一篇文章:数据库分库分表(一)常见分布式主键ID生成策略

package util;

/**
 * Twitter-snowflake负载均衡主键算法
 * 共分成4段:
 * 1.符号位( 1bit)
 * 2.时间戳(41bit):计算下来能够使用的期限为:2039-09-07 23:47:35.551,如果希望支持更久,将参考时间1970-01-01换成其它时间
 * 3.主机号(10bit):
 * 4.序列号(12bit):1毫秒内单机的并发最大为4096,超过4096则只能等待下一毫秒
 */

public class SnowflakeGenerator {
    //时间戳二进制位数
//    private final static int TIMESTAMP_BITS = 41;
    //主机号二进制位数
    private final static int SERVER_NODE_BITS = 10;
    //序列号二进制位数
    private final static int SEQUENCE_BITS = 12;


    //主机号:0~2**10-1
    private final static int MAX_SERVER_NODE = 1024;
    //序列号:0~2**12-1
    private final static int MAX_SEQUENCE = 4096;

    //当前主机节点[0,1024)
    private static int serverNode = 0;

    //序列号
    private static int sequence = 0;

    //上次生成id的时间
    private static long previousTimestamp = -1L;

    /**
     * 生成唯一主键
     * @return 64位long类型主键
     */
    public static synchronized long generate(int node){
        //初始化Snowflake主键生成器
        if (node < 0 || node >= MAX_SERVER_NODE) {
            throw new IllegalArgumentException(String.format("主机节点号范围为[%s and %s)", 0, MAX_SERVER_NODE));
        }
        serverNode = node;

        long timestamp;
        timestamp = System.currentTimeMillis();
        if(timestamp>previousTimestamp){
            sequence=0;
            previousTimestamp=timestamp;
        }else if(timestamp==previousTimestamp){
            if(++sequence==MAX_SEQUENCE){
                //序列号已到最大值,重置为0,此毫秒内不能再生成id,只能等下一毫秒
                sequence=0;
                timestamp=nextMillisecond();
                previousTimestamp=timestamp;
            }
        }else{
            throw new RuntimeException(String.format("当前时间戳 %s 小于 上次时间戳 %s",timestamp,previousTimestamp));
        }
        return timestamp<<SERVER_NODE_BITS<<SEQUENCE_BITS | serverNode<<SEQUENCE_BITS | sequence;
        //Java中的整型在计算机中是以二进制补码的形式存储的,最后这里需要了解补码及位运算的概念,位运算速度比十进制计算快很多
        //[参考教程] http://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
    }

    /**
     * 等待,直到下一毫秒
     * @return nextMillisecond
     */
    private static long nextMillisecond() {
        long nextMillisecond = System.currentTimeMillis();
        while (nextMillisecond <= previousTimestamp) {
            nextMillisecond = System.currentTimeMillis();
        }
        return nextMillisecond;
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        for(int i=0;i<1000000;i++){
            long l = SnowflakeGenerator.generate(1023);
            if(i==0 || i==1000000-1){
                System.out.println(l);
            }
        }
        long end = System.currentTimeMillis();
        long diff = end - start;
        System.out.println("用时:"+diff+"毫秒");
    }

}

推荐阅读更多精彩内容