parent
cd5a2fc68c
commit
0b346e9059
8 changed files with 415 additions and 18 deletions
@ -0,0 +1,146 @@ |
||||
package com.msdw.tms; |
||||
|
||||
/** |
||||
* @Author: gongsj. |
||||
* @Description: 自定义id生成器 |
||||
* @Date:Created in 2020/09/09 09:33. |
||||
* @Modified By: |
||||
*/ |
||||
public class IdWorker { |
||||
|
||||
//因为二进制里第一个 bit 为如果是 1,那么都是负数,但是我们生成的 id 都是正数,所以第一个 bit 统一都是 0。
|
||||
|
||||
//机器ID 2进制5位 32位减掉1位 31个
|
||||
private long workerId; |
||||
//机房ID 2进制5位 32位减掉1位 31个
|
||||
private long datacenterId; |
||||
//代表一毫秒内生成的多个id的最新序号 12位 4096 -1 = 4095 个
|
||||
private long sequence; |
||||
//设置一个时间初始值 2^41 - 1 差不多可以用69年
|
||||
private long twepoch = 1585644268888L; |
||||
//5位的机器id
|
||||
private long workerIdBits = 5L; |
||||
//5位的机房id
|
||||
private long datacenterIdBits = 5L; |
||||
//每毫秒内产生的id数 2 的 12次方
|
||||
private long sequenceBits = 12L; |
||||
// 这个是二进制运算,就是5 bit最多只能有31个数字,也就是说机器id最多只能是32以内
|
||||
private long maxWorkerId = -1L ^ (-1L << workerIdBits); |
||||
// 这个是一个意思,就是5 bit最多只能有31个数字,机房id最多只能是32以内
|
||||
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); |
||||
|
||||
private long workerIdShift = sequenceBits; |
||||
private long datacenterIdShift = sequenceBits + workerIdBits; |
||||
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; |
||||
private long sequenceMask = -1L ^ (-1L << sequenceBits); |
||||
//记录产生时间毫秒数,判断是否是同1毫秒
|
||||
private long lastTimestamp = -1L; |
||||
|
||||
public long getWorkerId() { |
||||
return workerId; |
||||
} |
||||
|
||||
public long getDatacenterId() { |
||||
return datacenterId; |
||||
} |
||||
|
||||
public long getTimestamp() { |
||||
return System.currentTimeMillis(); |
||||
} |
||||
|
||||
|
||||
public IdWorker(long workerId, long datacenterId, long sequence) { |
||||
|
||||
// 检查机房id和机器id是否超过31 不能小于0
|
||||
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; |
||||
this.sequence = sequence; |
||||
} |
||||
|
||||
// 这个是核心方法,通过调用nextId()方法,让当前这台机器上的snowflake算法程序生成一个全局唯一的id
|
||||
public synchronized long nextId() { |
||||
// 这儿就是获取当前时间戳,单位是毫秒
|
||||
long timestamp = timeGen(); |
||||
if (timestamp < lastTimestamp) { |
||||
|
||||
System.err.printf( |
||||
"clock is moving backwards. Rejecting requests until %d.", lastTimestamp); |
||||
throw new RuntimeException( |
||||
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", |
||||
lastTimestamp - timestamp)); |
||||
} |
||||
|
||||
// 下面是说假设在同一个毫秒内,又发送了一个请求生成一个id
|
||||
// 这个时候就得把seqence序号给递增1,最多就是4096
|
||||
if (lastTimestamp == timestamp) { |
||||
|
||||
// 这个意思是说一个毫秒内最多只能有4096个数字,无论你传递多少进来,
|
||||
//这个位运算保证始终就是在4096这个范围内,避免你自己传递个sequence超过了4096这个范围
|
||||
sequence = (sequence + 1) & sequenceMask; |
||||
//当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID
|
||||
if (sequence == 0) { |
||||
timestamp = tilNextMillis(lastTimestamp); |
||||
} |
||||
|
||||
} else { |
||||
sequence = 0; |
||||
} |
||||
// 这儿记录一下最近一次生成id的时间戳,单位是毫秒
|
||||
lastTimestamp = timestamp; |
||||
// 这儿就是最核心的二进制位运算操作,生成一个64bit的id
|
||||
// 先将当前时间戳左移,放到41 bit那儿;将机房id左移放到5 bit那儿;将机器id左移放到5 bit那儿;将序号放最后12 bit
|
||||
// 最后拼接起来成一个64 bit的二进制数字,转换成10进制就是个long型
|
||||
return ((timestamp - twepoch) << timestampLeftShift) | |
||||
(datacenterId << datacenterIdShift) | |
||||
(workerId << workerIdShift) | sequence; |
||||
} |
||||
|
||||
/** |
||||
* 当某一毫秒的时间,产生的id数 超过4095,系统会进入等待,直到下一毫秒,系统继续产生ID |
||||
* |
||||
* @param lastTimestamp |
||||
* @return |
||||
*/ |
||||
private long tilNextMillis(long lastTimestamp) { |
||||
|
||||
long timestamp = timeGen(); |
||||
|
||||
while (timestamp <= lastTimestamp) { |
||||
timestamp = timeGen(); |
||||
} |
||||
return timestamp; |
||||
} |
||||
|
||||
//获取当前时间戳
|
||||
private long timeGen() { |
||||
return System.currentTimeMillis(); |
||||
} |
||||
|
||||
/** |
||||
* main 测试类 |
||||
* |
||||
* @param args |
||||
*/ |
||||
public static void main(String[] args) { |
||||
// System.out.println(1&4596);
|
||||
// System.out.println(2&4596);
|
||||
// System.out.println(6&4596);
|
||||
// System.out.println(6&4596);
|
||||
// System.out.println(6&4596);
|
||||
// System.out.println(6&4596);
|
||||
IdWorker worker = new IdWorker(1, 1, 1); |
||||
for (int i = 0; i < 22; i++) { |
||||
System.out.println(worker.nextId()); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,174 @@ |
||||
package com.msdw.tms; |
||||
|
||||
/** |
||||
* @Author:JCccc |
||||
* @Description: |
||||
* @Date: created in 15:31 2019/6/12 |
||||
*/ |
||||
public class SnowflakeIdUtils { |
||||
// ==============================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 SnowflakeIdUtils(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(); |
||||
} |
||||
|
||||
//==============================Test=============================================
|
||||
|
||||
/** |
||||
* 测试 |
||||
*/ |
||||
public static void main(String[] args) { |
||||
SnowflakeIdUtils idWorker = new SnowflakeIdUtils(3, 1); |
||||
// System.out.println(idWorker.nextId());
|
||||
for (int i = 0; i < 20; i++) { |
||||
System.out.println(idWorker.nextId()); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue