diff --git a/Java/source/src/main/java/com/github/yitter/contract/IdGeneratorOptions.java b/Java/source/src/main/java/com/github/yitter/contract/IdGeneratorOptions.java index 3b24535..a1601b9 100644 --- a/Java/source/src/main/java/com/github/yitter/contract/IdGeneratorOptions.java +++ b/Java/source/src/main/java/com/github/yitter/contract/IdGeneratorOptions.java @@ -19,6 +19,8 @@ public class IdGeneratorOptions { /** * 基础时间(ms单位) * 不能超过当前系统时间 + * 默认值 1582136402000L,表示2020-02-20 00:00:02 + * 新项目建议改成 1735689600000L,表示2025-01-01 00:00:00 */ public long BaseTime = 1582136402000L; @@ -58,6 +60,12 @@ public class IdGeneratorOptions { */ public short TopOverCostCount = 2000; + /** + * 时间精度: 0 毫秒 1 10毫秒(实际上是8毫秒) 2 100毫秒(实际上是128毫秒) 3 1秒(实际上是1024毫秒) 4 10秒(实际上是8192毫秒) + * 默认毫秒,与原版兼容 + */ + public short Precision = 0; + public IdGeneratorOptions() { } diff --git a/Java/source/src/main/java/com/github/yitter/core/SnowWorkerM1.java b/Java/source/src/main/java/com/github/yitter/core/SnowWorkerM1.java index 9d77ad0..c434426 100644 --- a/Java/source/src/main/java/com/github/yitter/core/SnowWorkerM1.java +++ b/Java/source/src/main/java/com/github/yitter/core/SnowWorkerM1.java @@ -14,40 +14,47 @@ public class SnowWorkerM1 implements ISnowWorker { /** * 基础时间 */ - protected final long BaseTime; + protected long BaseTime; /** * 机器码 */ - protected final short WorkerId; + protected short WorkerId; /** * 机器码位长 */ - protected final byte WorkerIdBitLength; + protected byte WorkerIdBitLength; /** * 自增序列数位长 */ - protected final byte SeqBitLength; + protected byte SeqBitLength; /** * 最大序列数(含) */ - protected final int MaxSeqNumber; + protected int MaxSeqNumber; /** * 最小序列数(含) */ - protected final short MinSeqNumber; + protected short MinSeqNumber; /** * 最大漂移次数(含) */ - protected final int TopOverCostCount; + protected int TopOverCostCount; - protected final byte _TimestampShift; - protected final static byte[] _SyncLock = new byte[0]; + /** + * 移位位数 + */ + protected int ShiftBits; + + protected int _sleepTime; + + protected byte _TimestampShift; + protected final byte[] _SyncLock = new byte[0]; protected short _CurrentSeqNumber; protected long _LastTimeTick = 0; @@ -61,15 +68,43 @@ public class SnowWorkerM1 implements ISnowWorker { public SnowWorkerM1(IdGeneratorOptions options) { BaseTime = options.BaseTime != 0 ? options.BaseTime : 1582136402000L; - WorkerIdBitLength = options.WorkerIdBitLength == 0 ? 6 : options.WorkerIdBitLength; + WorkerIdBitLength = options.WorkerIdBitLength; WorkerId = options.WorkerId; SeqBitLength = options.SeqBitLength == 0 ? 6 : options.SeqBitLength; MaxSeqNumber = options.MaxSeqNumber <= 0 ? (1 << SeqBitLength) - 1 : options.MaxSeqNumber; MinSeqNumber = options.MinSeqNumber; // TopOverCostCount = options.TopOverCostCount == 0 ? 2000 : options.TopOverCostCount; TopOverCostCount = options.TopOverCostCount; + switch (options.Precision) { + case 0: + ShiftBits = 0; + break; + case 1: + ShiftBits = 3; + break; + case 2: + ShiftBits = 7; + break; + case 3: + ShiftBits = 10; + break; + case 4: + ShiftBits = 13; + break; + default: + ShiftBits = 0; + break; + } + // 计算睡眠时间,最大1秒 + _sleepTime = 1 << ShiftBits; + // 无需等到精度时间,分4次检查 + if (_sleepTime > 50) { + _sleepTime = _sleepTime >> 2; + } _TimestampShift = (byte) (WorkerIdBitLength + SeqBitLength); _CurrentSeqNumber = MinSeqNumber; + + BaseTime = BaseTime >> ShiftBits; } private void DoGenIdAction(OverCostActionArg arg) { @@ -207,7 +242,7 @@ private long CalcTurnBackId(long useTimeTick) { protected long GetCurrentTimeTick() { long millis = System.currentTimeMillis(); - return millis - BaseTime; + return (millis >> ShiftBits) - BaseTime; } protected long GetNextTimeTick() { @@ -215,7 +250,7 @@ protected long GetNextTimeTick() { while (tempTimeTicker <= _LastTimeTick) { try { - Thread.sleep(1); + Thread.sleep(_sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } diff --git a/Java/source/src/main/java/com/github/yitter/idgen/DefaultIdGenerator.java b/Java/source/src/main/java/com/github/yitter/idgen/DefaultIdGenerator.java index 2353006..59ef407 100644 --- a/Java/source/src/main/java/com/github/yitter/idgen/DefaultIdGenerator.java +++ b/Java/source/src/main/java/com/github/yitter/idgen/DefaultIdGenerator.java @@ -13,7 +13,7 @@ public class DefaultIdGenerator implements IIdGenerator { - private static ISnowWorker _SnowWorker = null; + private ISnowWorker _SnowWorker = null; public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorException { if (options == null) { @@ -26,8 +26,8 @@ public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorExceptio } // 2.WorkerIdBitLength - if (options.WorkerIdBitLength <= 0) { - throw new IdGeneratorException("WorkerIdBitLength error.(range:[1, 21])"); + if (options.WorkerIdBitLength < 0) { + throw new IdGeneratorException("WorkerIdBitLength error.(range:[0, 21])"); } if (options.WorkerIdBitLength + options.SeqBitLength > 22) { throw new IdGeneratorException("error:WorkerIdBitLength + SeqBitLength <= 22"); @@ -35,10 +35,14 @@ public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorExceptio // 3.WorkerId int maxWorkerIdNumber = (1 << options.WorkerIdBitLength) - 1; - if (maxWorkerIdNumber == 0) { - maxWorkerIdNumber = 63; + // if (maxWorkerIdNumber == 0) { + // maxWorkerIdNumber = 63; + // } + if (options.WorkerIdBitLength == 0 && options.WorkerId != 0) { + throw new IdGeneratorException( + "WorkerIdBitLength is 0, WorkerId must be 0"); } - if (options.WorkerId < 0 || options.WorkerId > maxWorkerIdNumber) { + else if (options.WorkerId < 0 || options.WorkerId > maxWorkerIdNumber) { throw new IdGeneratorException( "WorkerId error. (range:[0, " + (maxWorkerIdNumber > 0 ? maxWorkerIdNumber : 63) + "]"); } @@ -62,6 +66,10 @@ public DefaultIdGenerator(IdGeneratorOptions options) throws IdGeneratorExceptio throw new IdGeneratorException("MinSeqNumber error. (range:[5, " + maxSeqNumber + "]"); } + if (options.Precision < 0 || options.Precision > 4) { + throw new IdGeneratorException("Precision error. (range:[0, 4]"); + } + // 7.TopOverCostCount if (options.TopOverCostCount < 0 || options.TopOverCostCount > 10000) { throw new IdGeneratorException("TopOverCostCount error. (range:[0, 10000]"); diff --git a/Java/source/src/main/java/com/github/yitter/idgen/YitIdHelper.java b/Java/source/src/main/java/com/github/yitter/idgen/YitIdHelper.java index b501d0b..0b98150 100644 --- a/Java/source/src/main/java/com/github/yitter/idgen/YitIdHelper.java +++ b/Java/source/src/main/java/com/github/yitter/idgen/YitIdHelper.java @@ -4,6 +4,9 @@ */ package com.github.yitter.idgen; +import java.util.HashMap; +import java.util.Map; + import com.github.yitter.contract.IIdGenerator; import com.github.yitter.contract.IdGeneratorException; import com.github.yitter.contract.IdGeneratorOptions; @@ -13,17 +16,34 @@ */ public class YitIdHelper { - private static IIdGenerator idGenInstance = null; + private static final String DEFAULT_INSTANCE_NAME = "default"; + + private static final Map idGenInstanceMap = new HashMap<>(); public static IIdGenerator getIdGenInstance() { - return idGenInstance; + return idGenInstanceMap.get(DEFAULT_INSTANCE_NAME); + } + + public static IIdGenerator getIdGenInstance(String instanceName) { + return idGenInstanceMap.get(instanceName); } /** * 设置参数,建议程序初始化时执行一次 */ public static void setIdGenerator(IdGeneratorOptions options) throws IdGeneratorException { - idGenInstance = new DefaultIdGenerator(options); + idGenInstanceMap.put(DEFAULT_INSTANCE_NAME, new DefaultIdGenerator(options)); + } + + /** + * 设置参数,建议程序初始化时执行一次 + */ + public static void setIdGenerator(String instanceName, IdGeneratorOptions options) throws IdGeneratorException { + if (instanceName == null || instanceName.trim().isEmpty()) { + throw new IdGeneratorException("instanceName cannot be null or empty"); + } + + idGenInstanceMap.put(instanceName, new DefaultIdGenerator(options)); } /** @@ -37,9 +57,24 @@ public static long nextId() throws IdGeneratorException { // idGenInstance = new DefaultIdGenerator(new IdGeneratorOptions((short) 1)); // } - if (idGenInstance == null) + var generator = idGenInstanceMap.get(DEFAULT_INSTANCE_NAME); + if (generator == null) throw new IdGeneratorException("Please initialize Yitter.IdGeneratorOptions first."); - return idGenInstance.newLong(); + return generator.newLong(); + } + + /** + * 生成新的Id + * 调用本方法前,请确保调用了 SetIdGenerator 方法做初始化。 + * + * @return + */ + public static long nextId(String instanceName) throws IdGeneratorException { + var generator = idGenInstanceMap.get(instanceName); + if (generator == null) + throw new IdGeneratorException("Please initialize instance: " + instanceName + " with YitIdHelper.setIdGenerator(instanceName, options) first."); + + return generator.newLong(); } } diff --git a/README.md b/README.md index 64de23c..6314416 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,19 @@ # 雪花算法中非常好用的数字ID生成器 +Java代码相对于原版做了如下改动,适用于中小型项目生成更短和数值更小的ID: + +1. 支持不同的时间精度,默认是1毫秒,与原版兼容; +2. WorkIdBitLength支持为0,适用于单机版的小项目; +3. 支持多个generator instance,服务于不同的场景 + +与原版相比,改进版的ID长度最低可降低到9位,更有效的利用号段资源。 + +关于改进的更多说明及程序结果可参考博文: [一个改进的Java短ID生成库](https://itlanyan.com/an-improved-short-id-generator/) + + +------------- 以下是原版说明 -------------- + + ## 💎 最佳实践(置顶) 针对大家在使用中经常出现的性能疑问,我给出以下3组最佳实践: @@ -101,7 +115,7 @@ * +-------------------------+--------------+----------+ * | 1.相对基础时间的时间差 | 2.WorkerId | 3.序列数 | * +-------------------------+--------------+----------+ - * + * * 第1部分,时间差,是生成ID时的系统时间减去 BaseTime 的总时间差(毫秒单位)。 * 第2部分,WorkerId,是区分不同机器或不同应用的唯一ID,最大值由 WorkerIdBitLength(默认6)限定。 * 第3部分,序列数,是每毫秒下的序列数,由参数中的 SeqBitLength(默认6)限定。 @@ -242,20 +256,20 @@ extern void UnRegister(); ## 已实现的语言 -| 语言 | github | -| ---- | ---- | -| 🌲 C# | [查看示例][1] | -| 🌲 Java | [查看示例][2] | -| 🌲 Go| [查看示例][3] | -| 🌲 Rust | [查看示例][4] | -| 🌲 Python | [查看示例][10] | +| 语言 | github | +| ---- | ---- | +| 🌲 C# | [查看示例][1] | +| 🌲 Java | [查看示例][2] | +| 🌲 Go| [查看示例][3] | +| 🌲 Rust | [查看示例][4] | +| 🌲 Python | [查看示例][10] | | 🌲 C | [查看示例][5] | | 🌲 C (PHP扩展) | [查看示例][7] | | 🌲 Delphi (Pascal) | [查看示例][6] | | 🌲 JavaScript | [查看示例][8] | | 🌲 TypeScript | [查看示例][9] | -| 🌲 V | [查看示例][6] | -| 🌲 D | [查看示例][72] | +| 🌲 V | [查看示例][6] | +| 🌲 D | [查看示例][72] | ## 技术支持