Browse Source

fix Snoefake bug

Looly 5 years ago
parent
commit
6216eb9994

+ 11 - 9
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;
 		}

+ 25 - 2
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<Long> 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!");
+				}
+			}
+		});
+	}
 }