SnowflakeIdWorker
/**
* 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的更多相关文章
- 高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器
package xxx; import java.sql.Timestamp; import java.util.concurrent.*; import java.util.concurrent.a ...
- Twitter的分布式自增ID算法snowflake (Java版)
概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种 ...
- 分布式唯一id:snowflake算法思考
匠心零度 转载请注明原创出处,谢谢! 缘起 为什么会突然谈到分布式唯一id呢?原因是最近在准备使用RocketMQ,看看官网介绍: 一句话,消息可能会重复,所以消费端需要做幂等.为什么消息会重复后续R ...
- java 分布式id生成算法
import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.NetworkI ...
- ID 生成器 雪花算法
https://blog.csdn.net/wangming520liwei/article/details/80843248 ID 生成器 雪花算法 2018年06月28日 14:58:43 wan ...
- SnowFlake学习
分布式系统中生成全局唯一且趋势递增ID UUID - 太长,无序,数据库插入分裂性能不行 利用数据库自增序列,等步长生成 - 依赖数据库 SnowFlake:使用见下图 抄代码 https://www ...
- 唯一ID算法之:snowflake(Java版本)
Twitter开源的算法,简单易用. /** * Twitter_Snowflake<br> * SnowFlake的结构如下(每部分用-分开):<br> * 0 - 0000 ...
- UUID实现之一twitter的分布式自增IDsnowflake算法
Twitter的分布式自增ID算法snowflake (Java版) 概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点 ...
- Twitter的SnowFlake分布式id生成算法
二进制相关知识回顾 1.所有的数据都是以二进制的形式存储在硬盘上.对于一个字节的8位到底是什么类型 计算机是如何分辨的呢? 其实计算机并不负责判断数据类型,数据类型是程序告诉计算机该如何解释内存块. ...
随机推荐
- return、reutrn false、e.preventDefault、e.stopPropagation、e.stopImmediatePropagation的区别
return var i = function(){ return } console.log(i())//undefined return的主要作用是阻止函数继续执行,直接返回undefined r ...
- OkHttp3 readError问题解决
有些时候是服务端响应的太慢而本地链接又关闭引起的读取失败. 这时候可以调整本地链接关闭的时间. 例如以下设置超时关闭的时间为120秒. OkHttpClient okHttpClient = new ...
- linux 压缩、解压、zip/unzip/jar
网上很多人说用jar命令解压,但jar命令解压时不能指定目录,推荐使用unzip解压war包. unzip -d 指定目录 [root@oracle upload]# unzip -oq common ...
- Spring不能直接@autowired注入Static变量/ 关于SpringBoot的@Autowired 静态变量注入
昨天在编写JavaMail工具类的时候,静态方法调用静态变量,这是很正常的操作,当时也没多想,直接静态注入. @Component public class JavaMailUtil { @Autow ...
- 008_AuditionCC系列1
一.一二章编辑音频文件. (1)在音轨编辑页面,按鼠标滚轮或键盘上的+个-号可实现Zoom in(放大),Zoom out(缩小) (2)①次声波频率在<20Hz和>20000Hz之间②人 ...
- word2vec原理总结
一篇很好的入门博客,http://mccormickml.com/2016/04/19/word2vec-tutorial-the-skip-gram-model/ 他的翻译,https://www. ...
- PHPExcel 导出数据(xls或xlsx)- 助手类(函数)
本文链接:https://www.cnblogs.com/tujia/p/11358096.html 说明:简单好用的导出助手,轻松导出数据到 excel !! 使用示例: Example: Exce ...
- LODOP表格水平居中3(宽度为百分比)
如果一个表格在css样式等中设置了固定的宽度,想要实现表格在纸张中水平居中,可根据固定的宽度设置合适的左边距,如果打印项内容在打印项宽度中居中,可以设置打印项在纸张中居中.方法1:宽度固定,纸张大小固 ...
- 在Eclipse中设置中文JavaDOC
参考: 在Eclipse中设置中文JavaDOC
- python从写定时器学习Thread
目录 python从写定时器学习Thread Timer 对象 粗陋的循环定时器 更 pythonic 循环定时器 FAQ python从写定时器学习Thread python 如何写一个定时器,循环 ...