/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/

package cn.ucaner.alpaca.common.util.key;

/**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
public class SnowflakeIdWorker {
// ==============================Fields===========================================
/**
* 开始时间截 (2015-01-01)
*/
private final long twepoch = 1420041600000L; /**
* 机器id所占的位数
*/
private final long workerIdBits = 5L; /**
* 数据标识id所占的位数
*/
private final long datacenterIdBits = 5L; /**
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
*/
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /**
* 支持的最大数据标识id,结果是31
*/
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /**
* 序列在id中占的位数
*/
private final long sequenceBits = 12L; /**
* 机器ID向左移12位
*/
private final long workerIdShift = sequenceBits; /**
* 数据标识id向左移17位(12+5)
*/
private final long datacenterIdShift = sequenceBits + workerIdBits; /**
* 时间截向左移22位(5+5+12)
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /**
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
*/
private final long sequenceMask = -1L ^ (-1L << sequenceBits); /**
* 工作机器ID(0~31)
*/
private long workerId; /**
* 数据中心ID(0~31)
*/
private long datacenterId; /**
* 毫秒内序列(0~4095)
*/
private long sequence = 0L; /**
* 上次生成ID的时间截
*/
private long lastTimestamp = -1L; //==============================Constructors===================================== /**
* 构造函数
*
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
} // ==============================Methods========================================== /**
* 获得下一个ID (该方法是线程安全的)
*
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = timeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
} //如果是同一时间生成的,则进行毫秒内序列
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
//毫秒内序列溢出
if (sequence == 0) {
//阻塞到下一个毫秒,获得新的时间戳
timestamp = tilNextMillis(lastTimestamp);
}
}
//时间戳改变,毫秒内序列重置
else {
sequence = 0L;
} //上次生成ID的时间截
lastTimestamp = timestamp; //移位并通过或运算拼到一起组成64位的ID
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
} /**
* 阻塞到下一个毫秒,直到获得新的时间戳
*
* @param lastTimestamp 上次生成ID的时间截
* @return 当前时间戳
*/
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
} /**
* 返回以毫秒为单位的当前时间
*
* @return 当前时间(毫秒)
*/
protected long timeGen() {
//return System.currentTimeMillis();
return SystemClock.now();
} //==============================Test============================================= /**
* 测试
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
SnowflakeIdWorker idWorker0 = new SnowflakeIdWorker(0, 0);
for (int i = 0; i < 10; i++) {
long id = idWorker0.nextId();
System.out.println(id);
}
System.out.println("耗时:" + (System.currentTimeMillis() - start));
} }
//Outputs
//444159897148325888
//444159897148325889
//444159897148325890
//444159897148325891
//444159897148325892
//444159897148325893
//444159897148325894
//444159897148325895
//444159897148325896
//444159897148325897
//耗时:7 //444159955377848320
//444159955377848321
//444159955377848322
//444159955377848323
//444159955377848324
//444159955377848325
//444159955377848326
//444159955377848327
//444159955377848328
//444159955377848329
//耗时:7

SnowflakeIdWorker的更多相关文章

  1. 高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器

    package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.a ...

  2. Twitter的分布式自增ID算法snowflake (Java版)

    概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...

  3. 分布式唯一id:snowflake算法思考

    匠心零度 转载请注明原创出处,谢谢! 缘起 为什么会突然谈到分布式唯一id呢?原因是最近在准备使用RocketMQ,看看官网介绍: 一句话,消息可能会重复,所以消费端需要做幂等.为什么消息会重复后续R ...

  4. java 分布式id生成算法

    import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkI ...

  5. ID 生成器 雪花算法

    https://blog.csdn.net/wangming520liwei/article/details/80843248 ID 生成器 雪花算法 2018年06月28日 14:58:43 wan ...

  6. SnowFlake学习

    分布式系统中生成全局唯一且趋势递增ID UUID - 太长,无序,数据库插入分裂性能不行 利用数据库自增序列,等步长生成 - 依赖数据库 SnowFlake:使用见下图 抄代码 https://www ...

  7. 唯一ID算法之:snowflake(Java版本)

    Twitter开源的算法,简单易用. /** * Twitter_Snowflake<br> * SnowFlake的结构如下(每部分用-分开):<br> * 0 - 0000 ...

  8. UUID实现之一twitter的分布式自增IDsnowflake算法

    Twitter的分布式自增ID算法snowflake (Java版)   概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点 ...

  9. Twitter的SnowFlake分布式id生成算法

    二进制相关知识回顾 1.所有的数据都是以二进制的形式存储在硬盘上.对于一个字节的8位到底是什么类型 计算机是如何分辨的呢? 其实计算机并不负责判断数据类型,数据类型是程序告诉计算机该如何解释内存块. ...

随机推荐

  1. 解决wordpress修改固定链接后出现404错误不能访问文章的方法

    首先,建议大家安装完wordpress网站程序之后第一时间设置一下固定链接,避免以后修改出错.在wp后台仪表盘左侧导航里找到“设置——固定链接”即可配置你的wordpress固定链接,通常我习惯使用的 ...

  2. ansible-playbook-批量修改主机名

    修改cat /etc/ansible/hosts [test]10.27.235.108 host_name=test_host_name - hosts: test user: root gathe ...

  3. 【Beta】Phylab 发布说明

    Phylab Beta阶段发布说明 一.发布地址 Phylab 二.新功能 1. Markdown(Html)报告生成 在生成报告界面可以选择报告生成方式:Latex或Markdown模板.目前支持M ...

  4. Google BERT应用之《红楼梦》对话人物提取

    Google BERT应用之<红楼梦>对话人物提取 https://www.jiqizhixin.com/articles/2019-01-24-19

  5. Linux系统下zookeeper客户端命令使用

    1. 启动客户端 [admin@yrjk bin]$ ./zkCli.sh [zk: localhost:2181(CONNECTED) 0] 2. 显示所有操作命令 [zk: localhost:2 ...

  6. BaiduPCS-Go的安装及使用

    BaiduPCS-Go的安装及使用 linux下会提示输入验证码,浏览器打开验证码url,多输入几次 Contents [hide] 一. 软件下载及安装 二. 软件的使用 1. 账号登录与退出 2. ...

  7. 搭建Portainer可视化界面(转)

    转载地址:https://blog.csdn.net/u011781521/article/details/80469804 一.什么是Portainer? Portainer是Docker的图形化管 ...

  8. 深度相机Astra Pro测试教程

    最近在微信群内,很多群友在群友的推荐下,购买了Astra pro的深度相机,价格地道,物超所值!群友反馈积极,所以这里出一波简单的教程.   以下内容知识抛砖引玉,主要讲解windows下和Ubunt ...

  9. Qt开发经验小技巧61-70

    很多人问Qt嵌入式平台用哪个好,这里统一回答(当前时间节点2018年):imx6+335x比较稳定,性能高就用RK3288 RK3399,便宜的话就用全志H3,玩一玩可以用树莓派香橙派. 对于大段的注 ...

  10. 浏览器查看和手动设置cookie的值

    1.查看:按F12进入浏览器的开发者模式——console——在命令行输入javascript:alert(document.cookie),再回车 2.按F12进入浏览器的开发者模式——consol ...