From 6216eb99944782b90ec47546ad3d7cca7c32d715 Mon Sep 17 00:00:00 2001 From: Looly Date: Tue, 10 Nov 2020 11:28:09 +0800 Subject: [PATCH] fix Snoefake bug --- .../java/cn/hutool/core/lang/Snowflake.java | 20 +++++++------- .../cn/hutool/core/lang/SnowflakeTest.java | 27 +++++++++++++++++-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java b/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java index ba031fec5..ce52b7b9d 100644 --- a/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java +++ b/hutool-core/src/main/java/cn/hutool/core/lang/Snowflake.java @@ -33,11 +33,11 @@ public class Snowflake implements Serializable { private final long twepoch; private final long workerIdBits = 5L; - private final long dataCenterIdBits = 5L; - //// 最大支持机器节点数0~31,一共32个 - // 最大支持数据中心节点数0~31,一共32个 + // 最大支持机器节点数0~31,一共32个 @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"}) private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + private final long dataCenterIdBits = 5L; + // 最大支持数据中心节点数0~31,一共32个 @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"}) private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits); // 序列号12位 @@ -48,8 +48,9 @@ public class Snowflake implements Serializable { private final long dataCenterIdShift = sequenceBits + workerIdBits; // 时间毫秒数左移22位 private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits; - @SuppressWarnings({"PointlessBitwiseExpression", "FieldCanBeLocal"}) - private final long sequenceMask = -1L ^ (-1L << sequenceBits);// 4095 + // 序列掩码,用于限定序列最大值不能超过4095 + @SuppressWarnings("FieldCanBeLocal") + private final long sequenceMask = ~(-1L << sequenceBits);// 4095 private final long workerId; private final long dataCenterId; @@ -140,8 +141,8 @@ public class Snowflake implements Serializable { */ public synchronized long nextId() { long timestamp = genTime(); - if (timestamp < lastTimestamp) { - if(lastTimestamp - timestamp < 2000){ + if (timestamp < this.lastTimestamp) { + if(this.lastTimestamp - timestamp < 2000){ // 容忍2秒内的回拨,避免NTP校时造成的异常 timestamp = lastTimestamp; } else{ @@ -150,11 +151,12 @@ public class Snowflake implements Serializable { } } - if (timestamp == lastTimestamp) { - sequence = (sequence + 1) & sequenceMask; + if (timestamp == this.lastTimestamp) { + final long sequence = (this.sequence + 1) & sequenceMask; if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp); } + this.sequence = sequence; } else { sequence = 0L; } diff --git a/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java b/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java index c46b72302..795dddde4 100644 --- a/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/lang/SnowflakeTest.java @@ -1,10 +1,17 @@ package cn.hutool.core.lang; -import java.util.HashSet; - +import cn.hutool.core.collection.ConcurrentHashSet; +import cn.hutool.core.exceptions.UtilException; +import cn.hutool.core.thread.ConcurrencyTester; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.IdUtil; import org.junit.Assert; +import org.junit.Ignore; import org.junit.Test; +import java.util.HashSet; +import java.util.Set; + /** * Snowflake单元测试 * @author Looly @@ -43,4 +50,20 @@ public class SnowflakeTest { Assert.assertEquals(2, idWorker.getDataCenterId(nextId)); Assert.assertTrue(idWorker.getGenerateDateTime(nextId) - System.currentTimeMillis() < 10); } + + @Test + @Ignore + public void uniqueTest(){ + // 测试并发环境下生成ID是否重复 + Snowflake snowflake = IdUtil.createSnowflake(0, 0); + + Set ids = new ConcurrentHashSet<>(); + ConcurrencyTester tester = ThreadUtil.concurrencyTest(100, () -> { + for (int i = 0; i < 5000; i++) { + if(false == ids.add(snowflake.nextId())){ + throw new UtilException("重复ID!"); + } + } + }); + } }