雪花算法根据时间戳生成有序的 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. 【SpringBoot】02.编写HelloWorld

    1.编写一个返回HelloWorld的Controller @Controller public class HelloWorld { @ResponseBody @RequestMapping(&q ...

  2. C#泛型用法,附代码示列

    C#中的泛型,大致有如下几种:泛型方法,泛型类,泛型接口,泛型委托,泛型约束等 使用泛型的好处一个是为了减少代码量,更主要的是能提高效率,不需要进行拆箱和装箱的操作. 下面是写的简单的例子,共勉,加深 ...

  3. 安装使用Pycharm及Anaconda最全教程

    网上安装anaconda和pycharm的教程很多,然而很少有人能够很详细地讲解,特别是对于pycharm的虚拟环境相关的说明很少,我也是懵逼的用了两年多,经常发现之前pycharm安装的第三方库,明 ...

  4. Flutter 开发从 0 到 1(五)源码

    Flutter 开发从 0 到 1 明天开始又要上班了,你的假期任务完成如何啊?由于平时加班太多了,实在挤不出更多时间,从开始想用 Flutter <Flutter 开发从 0 到 1(一)需求 ...

  5. 线程池 ThreadPoolExecutor 原理及源码笔记

    前言 前面在学习 JUC 源码时,很多代码举例中都使用了线程池 ThreadPoolExecutor,并且在工作中也经常用到线程池,所以现在就一步一步看看,线程池的源码,了解其背后的核心原理. 公众号 ...

  6. linux 命令之file

    Linux file命令用于辨识文件类型. 通过file指令,我们得以辨识该文件的类型. 语法 file [-bcLvz][-f <名称文件>][-m <魔法数字文件>...] ...

  7. 一:JavaWeb

    1.javaWeb技术体系 2.HTMl 超文本标记语言 (超文本的意思就是除了可以包含文字之外,还可以包含图片链接音乐视频等...) 2.1 HTML网页的组成  (结构:HTML 表现:CSS 行 ...

  8. shell中if/seq/for/while/until

    1.if语句格式:  if 判断条件:then statement1 statement2 fi; 例子: 判断/test/a普通文件是否存在,存在则输出yes,不存在则输出no,并创建.  #! / ...

  9. 对图片进行Base64转码和解码

    Base64代码 base64.c #include <stdlib.h> #include <stdio.h> #include <string.h> #incl ...

  10. Git本地仓库和远程仓库冲突解决

    场景描述: 在本地创建了一个git repo,并且执行了,git init命令,创建了.gitignore文件,或者README.md文件: 在远程创建了一个git repo,创建时也初始化了.git ...