雪花算法根据时间戳生成有序的 64 bit 的 Long 类型的唯一 ID

各 bit 含义:

  • 1 bit: 符号位,0 是正数 1 是负数, ID 为正数,所以恒取 0
  • 41 bit: 时间差,我们可以选择一个参考点,用它来计算与当前时间的时间差 (毫秒数),41 bit 存储时间差,足够使用 69 年
  • 10 bit: 机器码,能编码 1024 台机器;可以手动指定含义,比如前5 bit 作为机器编号、后 5 bit 作为进程编号
  • 12 bit: 序列号,同一机器同一毫秒内产生不同的序列号,12 bit 可以支持 4096 个序列号

优点:

  • 灵活配置:机器码可以根据需求灵活配置含义
  • 无需持久化:如果序号自增往往需要持久化,本算法不需要持久化
  • ID 有含义/可逆性:ID 可以反解出来,对 ID 进行统计分析,可以很简单的分析出整个系统的繁忙曲线,还可以定位到每个机器,在某段时间承担了多少工作,分析出负载均衡情况
  • 高性能:生成速度很快
public class Snowflake {

    /**
* 每一部分所占位数
*/
private final long unusedBits = 1L;
private final long timestampBits = 41L;
private final long datacenterIdBits = 5L;
private final long workerIdBits = 5L;
private final long sequenceBits = 12L; /**
* 向左的位移
*/
private final long timestampShift = sequenceBits + datacenterIdBits + workerIdBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long workerIdShift = sequenceBits; /**
* 起始时间戳,初始化后不可修改
*/
private final long epoch = 1451606400000L; // 2016-01-01 /**
* 数据中心编码,初始化后不可修改
* 最大值: 2^5-1 取值范围: [0,31]
*/
private final long datacenterId; /**
* 机器或进程编码,初始化后不可修改
* 最大值: 2^5-1 取值范围: [0,31]
*/
private final long workerId; /**
* 序列号
* 最大值: 2^12-1 取值范围: [0,4095]
*/
private long sequence = 0L; /** 上次执行生成 ID 方法的时间戳 */
private long lastTimestamp = -1L; /*
* 每一部分最大值
*/
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 2^5-1
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); // 2^5-1
private final long maxSequence = -1L ^ (-1L << sequenceBits); // 2^12-1 /**
* 生成序列号
*/
public synchronized long nextId() {
long currTimestamp = timestampGen(); if (currTimestamp < lastTimestamp) {
throw new IllegalStateException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - currTimestamp));
} if (currTimestamp == lastTimestamp) {
sequence = (sequence + 1) & maxSequence;
if (sequence == 0) { // overflow: greater than max sequence
currTimestamp = waitNextMillis(currTimestamp);
} } else { // reset to 0 for next period/millisecond
sequence = 0L;
} // track and memo the time stamp last snowflake ID generated
lastTimestamp = currTimestamp; return ((currTimestamp - epoch) << timestampShift) | //
(datacenterId << datacenterIdShift) | //
(workerId << workerIdShift) | // new line for nice looking
sequence;
} public Snowflake(long datacenterId, long workerId) {
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(
String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
} this.datacenterId = datacenterId;
this.workerId = workerId;
} /**
* 追踪调用 waitNextMillis 方法的次数
*/
private final AtomicLong waitCount = new AtomicLong(0); public long getWaitCount() {
return waitCount.get();
} /**
* 循环阻塞直到下一秒
*/
protected long waitNextMillis(long currTimestamp) {
waitCount.incrementAndGet();
while (currTimestamp <= lastTimestamp) {
currTimestamp = timestampGen();
}
return currTimestamp;
} /**
* 获取当前时间戳
*/
public long timestampGen() {
return System.currentTimeMillis();
}
}

参考:snowflake ID 生成算法

完整代码:GitHub

雪花算法 Java 版的更多相关文章

  1. 排序算法Java版,以及各自的复杂度,以及由堆排序产生的top K问题

    常用的排序算法包括: 冒泡排序:每次在无序队列里将相邻两个数依次进行比较,将小数调换到前面, 逐次比较,直至将最大的数移到最后.最将剩下的N-1个数继续比较,将次大数移至倒数第二.依此规律,直至比较结 ...

  2. 排序算法系列:插入排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)

    在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 我总结一个清晰不罗嗦版: 原理: 和选择排序类似的是也分成“已排序”部分,和“未排 ...

  3. 排序算法系列:选择排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)

    在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 我总结一个清晰不罗嗦版: 原理: 从数组头元素索引i开始,寻找后面最小的值(比i位 ...

  4. 排序算法系列:快速排序算法JAVA版(靠谱、清晰、真实、可用、不罗嗦版)

    在网上搜索算法的博客,发现一个比较悲剧的现象非常普遍: 原理讲不清,混乱 啰嗦 图和文对不上 不可用,甚至代码还出错 为了不误人子弟耽误时间,推荐看一些靠谱的资源,如[啊哈!算法]系列: https: ...

  5. 常用排序算法--java版

    package com.whw.sortPractice; import java.util.Arrays; public class Sort { /** * 遍历一个数组 * @param sor ...

  6. Kruskal算法java版

    /** * sample Kruskal.java Description: * kruskal算法的思想是找最小边,且每次找到的边不会和以找出来的边形成环路,利用一个一维数组group存放当前顶点所 ...

  7. 快速排序算法Java版

    网上关于快速排序的算法原理和算法实现都比较多,不过java是实现并不多,而且部分实现很难理解,和思路有点不搭调.所以整理了这篇文章.如果有不妥之处还请建议.首先先复习一些基础.    1.算法概念. ...

  8. 爬山算法 | Java版HA_TSP

    嗯哼,今天记录下采用Java编写的爬山算法(Hill Algorithm)求解TSP问题. 爬山算法与其他智能算法类似,是一种用来求解多峰函数最值的算法,爬山算法的基本思想是新解不劣于当前解则转移,否 ...

  9. A*(也叫A star, A星)寻路算法Java版

    寻路算法有非常多种,A*寻路算法被公觉得最好的寻路算法. 首先要理解什么是A*寻路算法,能够參考这三篇文章: http://www.gamedev.net/page/resources/_/techn ...

随机推荐

  1. Spring框架之beans源码完全解析

    导读:Spring可以说是Java企业开发里最重要的技术.而Spring两大核心IOC(Inversion of Control控制反转)和AOP(Aspect Oriented Programmin ...

  2. 记EF的一个基本访问类

    代码: 1 using EFModel; 2 using System; 3 using System.Collections.Generic; 4 using System.Data.Entity; ...

  3. 凯撒密码(Java)

    事实上就是把每个字母偏移一下而已,并且字符不限于a-zA-z,可以是别的,那就很显而易见了,代码如下:定义一个Caesar密码类,成员变量只有密钥,也就是偏移量key 代码如下: public cla ...

  4. leetcode146 longest-substring-without-repeating-character

    题目描述 给定一个字符串,找出最长的不具有重复字符的子串的长度.例如,"abcabcbb"不具有重复字符的最长子串是"abc",长度为3.对于"bbb ...

  5. 为什么重写 equals() 方法,一定要重写 hashCode() 呢?| HashMap

    微信搜索「码农田小齐」,关注这个在纽约的程序媛,回复「01-05」可以获取计算机精选书籍.个人刷题笔记.大厂面经.面试资料等资源,么么哒- 首先我们有一个假设:任何两个 object 的 hashCo ...

  6. java开发就业信息管理系统

    本文实例为大家分享了java就业信息管理平台开发案例,供大家参考,具体内容如下 可查询公司信息,学生信息,班级信息,针对学生就业与否信息的统计,老师和管理员登录后的权限不同等就业信息管理平台想要实现的 ...

  7. 想更改Github仓库中的某个文件结构

    虽然有各种版本回退啥的,可是感觉好麻烦,还是没搞来,后来发现可以直接先删除,然后在本地更改,更改完之后重新添加一次即可 删除远程库的某个文件: $ git pull origin master 将远程 ...

  8. Python学习笔记2:基本数据类型

    Python中的变量不需要声明.每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建. 在 Python 中,变量就是变量,它没有类型,我们所说的"类型"是变量所指的内存中对象 ...

  9. 学习笔记——make项目中克隆GitHub目录失败的解决

    在示例项目中执行make后出现下面的错误 WARNING: Missing submodule components/json/cJSON... WARNING: Missing submodule ...

  10. python中的时间和时间格式转换

    1.python中的时间:要得到年月日时分秒的时间: import time #time.struct_time(tm_year=2012, tm_mon=9, tm_mday=15, tm_hour ...