手撸雪花算法

package com.delta.duid.snowflake;

/**
 * 雪花算法,普通版本,时间回滚无法处理 
* User: Delta * Date: 2019/11/2 */ public class Snowflake { /** * 初始时间戳 */ private final static long START_STMP = 1480166465631L; /** * 每段位数 */ private final static long SEQUENCE_BIT = 12;//序列号段所占位数 private final static long MACHINE_BIT = 5;//机器标识段所占位数 private final static long DATACENTER_BIT = 5;//机房标识段所占位数 /** * 每段位数的最大值
* 负数在计算机存储是,取负数绝对值,然后取反后加1。
* -1L 二进制存储是64个1。
* -1L << 5 = 1111111111111111111111111111111111111111111111111111111111100000 = -32
* -1L^-32
* 相当于下面两值异或(xor,不同为1,相同为0)
* 1111111111111111111111111111111111111111111111111111111111100000
* xor
* 1111111111111111111111111111111111111111111111111111111111111111
* =
* 0000000000000000000000000000000000000000000000000000000000011111 = 31= 2^5 -1
*

* 计算长度使用位操作而不是使用乘法,是因为乘除效率比较低。
*/ private final static long MAX_SEQUENCE_NUM = -1L ^ (-1L << SEQUENCE_BIT); private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT); /** * 每段左移的长度 */ private final static long LEFT_SEQUENCE_BIT = 0L;//序列号段需要左移位数 private final static long LEFT_MACHINE_BIT = LEFT_SEQUENCE_BIT + SEQUENCE_BIT;//机器标识段所需左移位数 private final static long LEFT_DATACENTER_BIT = LEFT_MACHINE_BIT + MACHINE_BIT;//机房标识段所需左移位数 private final static long LEFT_TIMESTMP_BIT = LEFT_DATACENTER_BIT + DATACENTER_BIT;//时间戳所需左移位数 /** * 成员变量 */ private long datacenterId;//机房标识 private long machineId;//机器标识 private long sequenceId = 0L;//序列号 private long lastStmp = -1L;//上一个时间戳 /** * 构造函数,检测机房标识和机器标识的正确性 * * @param datacenterId 机房标识 * @param machineId 机器标识 */ public Snowflake(long datacenterId, long machineId) { if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0L) { throw new IllegalArgumentException("机房标识不在0~" + MAX_DATACENTER_NUM + "之间"); } if (machineId > MAX_MACHINE_NUM || machineId < 0L) { throw new IllegalArgumentException("机器标识不在0~" + MAX_MACHINE_NUM + "之间"); } this.datacenterId = datacenterId; this.machineId = machineId; } /** * 时间出现回滚,会导致ID生成失败 * * @return 返回ID */ public synchronized long nextId() { long currentStmp = getNewStmp(); if (currentStmp < lastStmp) { throw new RuntimeException("时间出现回滚,不建议再生成ID"); } if (currentStmp == getNewStmp()) { //相同毫秒内序列号自增 sequenceId = (sequenceId + 1) & MAX_SEQUENCE_NUM; //同一毫秒内,序列号达到最大值 111B + 1 = 1000B & 111B = 0000B = 0 if (sequenceId == 0L) { currentStmp = getNextStmp(); } } else { //不同毫秒内,序列号从0开始 sequenceId = 0L; } lastStmp = currentStmp; //通过或运算将对应的值设置到对应的位数,时间取的是当前时间距离开始时间的毫秒数 return (currentStmp - START_STMP) << LEFT_TIMESTMP_BIT | datacenterId << LEFT_DATACENTER_BIT | machineId << LEFT_MACHINE_BIT | sequenceId << LEFT_SEQUENCE_BIT; } /** * @return 返回当前系统时间戳(毫秒) */ private long getNewStmp() { return System.currentTimeMillis(); } /** * 通过阻塞返回下一个时间戳 * * @return 返回下一个系统时间戳(毫秒) */ private long getNextStmp() { long stmp; do { stmp = getNewStmp(); } while (stmp <= lastStmp); return stmp; } public static void main(String[] args) { Snowflake snowflake = new Snowflake(1, 1); cost(snowflake); } private static void cost(Snowflake snowflake) { long start = System.currentTimeMillis(); for (int i = 0; i < 4000000; i++) { snowflake.nextId(); } System.out.println("耗时:" + (System.currentTimeMillis() - start)); } }

你可能感兴趣的:(手撸雪花算法)