From 62e376980254aa589864311a351b2d879ee7b156 Mon Sep 17 00:00:00 2001 From: ZhouXY108 Date: Fri, 27 Dec 2024 16:32:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=20ID=20=E7=94=9F=E6=88=90?= =?UTF-8?q?=E5=99=A8=E7=9A=84=E5=8D=95=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProgressOfTesting.txt | 10 +-- .../commons/util/SnowflakeIdGenerator.java | 77 +++++++++---------- .../commons/util/AssertToolsTests.java | 3 +- .../commons/util/IdGeneratorTests.java | 65 ++++++++++++++++ 4 files changed, 107 insertions(+), 48 deletions(-) create mode 100644 src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java diff --git a/ProgressOfTesting.txt b/ProgressOfTesting.txt index fe401c4..7223d75 100644 --- a/ProgressOfTesting.txt +++ b/ProgressOfTesting.txt @@ -1,6 +1,6 @@ -[ ] 未开始测试 - 13 (19.40%) +[ ] 未开始测试 - 10 (14.93%) [-] 测试未完成 - 9 (13.43%) -[Y] 测试完成 - 24 (35.82%) +[Y] 测试完成 - 27 (40.30%) [x] 无需测试 - 21 (31.34%) xyz.zhouxy.plusone.commons @@ -87,12 +87,12 @@ xyz.zhouxy.plusone.commons DateTimeTools.java [-] Enumeration.java [Y] EnumTools.java [Y] - IdGenerator.java [ ] - IdWorker.java [ ] + IdGenerator.java [Y] + IdWorker.java [Y] Numbers.java [Y] OptionalTools.java [Y] RandomTools.java [ ] RegexTools.java [ ] - SnowflakeIdGenerator.java [ ] + SnowflakeIdGenerator.java [Y] StringTools.java [Y] TreeBuilder.java [Y] diff --git a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java index da9f71e..c8af751 100644 --- a/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java +++ b/src/main/java/xyz/zhouxy/plusone/commons/util/SnowflakeIdGenerator.java @@ -64,9 +64,6 @@ public class SnowflakeIdGenerator { /** 上次生成 ID 的时间截 */ private long lastTimestamp = -1L; - /** 锁对象 */ - private final Object lock = new Object(); - // ==============================Constructors===================================== /** @@ -90,51 +87,47 @@ public class SnowflakeIdGenerator { * * @return SnowflakeId */ - public long nextId() { - long timestamp; - synchronized (lock) { - timestamp = timeGen(); + public synchronized long nextId() { + long timestamp = timeGen(); - // 发生了回拨,此刻时间小于上次发号时间 - if (timestamp < lastTimestamp) { - long offset = lastTimestamp - timestamp; - if (offset <= 5) { - // 时间偏差大小小于5ms,则等待两倍时间 - try { - TimeUnit.MILLISECONDS.sleep(offset << 1); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(e); - } - timestamp = timeGen(); - if (timestamp < lastTimestamp) { - // 还是小于,抛异常上报 - throwClockBackwardsEx(lastTimestamp, timestamp); - } - } else { + // 发生了回拨,此刻时间小于上次发号时间 + if (timestamp < lastTimestamp) { + long offset = lastTimestamp - timestamp; + if (offset <= 5) { + // 时间偏差大小小于5ms,则等待两倍时间 + try { + TimeUnit.MILLISECONDS.sleep(offset << 1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(e); + } + timestamp = timeGen(); + if (timestamp < lastTimestamp) { + // 还是小于,抛异常上报 throwClockBackwardsEx(lastTimestamp, timestamp); } + } else { + throwClockBackwardsEx(lastTimestamp, timestamp); } - - // 如果是同一时间生成的,则进行毫秒内序列 - if (lastTimestamp == timestamp) { - sequence = (sequence + 1) & SEQUENCE_MASK; - // 毫秒内序列溢出 - if (sequence == 0) { - // 阻塞到下一个毫秒,获得新的时间戳 - timestamp = tilNextMillis(lastTimestamp); - } - } - // 时间戳改变,毫秒内序列重置 - else { - sequence = 0L; - } - - // 上次生成 ID 的时间截 - lastTimestamp = timestamp; - } + // 如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & SEQUENCE_MASK; + // 毫秒内序列溢出 + if (sequence == 0) { + // 阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + // 时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + // 上次生成 ID 的时间截 + lastTimestamp = timestamp; + // 移位并通过或运算拼到一起组成64位的ID return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | datacenterIdAndWorkerId | sequence; } diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java index 6623b9c..b6ea20e 100644 --- a/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/AssertToolsTests.java @@ -925,9 +925,10 @@ public class AssertToolsTests { // #region - Condition + static final class MyException extends RuntimeException {} + @Test void testCheckCondition() { - class MyException extends RuntimeException {} AssertTools.checkCondition(true, MyException::new); diff --git a/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java new file mode 100644 index 0000000..55edba9 --- /dev/null +++ b/src/test/java/xyz/zhouxy/plusone/commons/util/IdGeneratorTests.java @@ -0,0 +1,65 @@ +package xyz.zhouxy.plusone.commons.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.junit.jupiter.api.Test; + +import cn.hutool.core.collection.ConcurrentHashSet; + +public class IdGeneratorTests { + + final ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + + @Test + void testSnowflakeIdGenerator() { // NOSONAR + final SnowflakeIdGenerator snowflake = new SnowflakeIdGenerator(0, 0); + final Set ids = new ConcurrentHashSet<>(); + for (int i = 0; i < 10000; i++) { + executor.execute(() -> { + for (int j = 0; j < 50000; j++) { + if (false == ids.add(snowflake.nextId())) { + throw new RuntimeException("重复ID!"); + } + } + }); + } + } + + @Test + void testIdWorker() { // NOSONAR + final IdWorker idWorker = new IdWorker(0L); + final Set ids = new ConcurrentHashSet<>(); + for (int i = 0; i < 10000; i++) { + executor.execute(() -> { + for (int j = 0; j < 50000; j++) { + if (false == ids.add(idWorker.nextId())) { + throw new RuntimeException("重复ID!"); + } + } + }); + executor.execute(() -> { + for (int j = 0; j < 50000; j++) { + if (false == ids.add(IdGenerator.nextSnowflakeId(0))) { + throw new RuntimeException("重复ID!"); + } + } + }); + } + } + + @Test + void testToSimpleString() { + UUID id = UUID.randomUUID(); + assertEquals(id.toString().replaceAll("-", ""), + IdGenerator.toSimpleString(id)); + } + +}