支持多线程生成唯一ID的订单雪花代码
package com.example.orm.utils;
import java.net.NetworkInterface;
import java.security.SecureRandom;
import java.time.Instant;
import java.util.Enumeration;
/**
* Distributed Sequence Generator.
* Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010
*
* This class should be used as a Singleton.
* Make sure that you create and reuse a Single instance of Snowflake per node in your distributed system cluster.
*/
public class Snowflake {
private static Snowflake instance = null;
static {
instance = new Snowflake();
}
private static final int UNUSED_BITS = 1; // Sign bit, Unused (always set to 0)
private static final int EPOCH_BITS = 41;
private static final int NODE_ID_BITS = 10;
private static final int SEQUENCE_BITS = 12;
private static final long maxNodeId = (1L << NODE_ID_BITS) - 1;
private static final long maxSequence = (1L << SEQUENCE_BITS) - 1;
// Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z)
private static final long DEFAULT_CUSTOM_EPOCH = 1695196591000L;
private final long nodeId;
private final long customEpoch;
private volatile long lastTimestamp = -1L;
private volatile long sequence = 0L;
// Create Snowflake with a nodeId and custom epoch
public Snowflake(long nodeId, long customEpoch) {
if(nodeId < 0 || nodeId > maxNodeId) {
throw new IllegalArgumentException(String.format("NodeId must be between %d and %d", 0, maxNodeId));
}
this.nodeId = nodeId;
this.customEpoch = customEpoch;
}
// Create Snowflake with a nodeId
public Snowflake(long nodeId) {
this(nodeId, DEFAULT_CUSTOM_EPOCH);
}
// Let Snowflake generate a nodeId
public Snowflake() {
this.nodeId = createNodeId();
this.customEpoch = DEFAULT_CUSTOM_EPOCH;
}
public static Snowflake getInstance() {
return instance;
}
public synchronized long nextId() {
long currentTimestamp = timestamp();
if(currentTimestamp < lastTimestamp) {
throw new IllegalStateException("Invalid System Clock!");
}
if (currentTimestamp == lastTimestamp) {
sequence = (sequence + 1) & maxSequence;
if(sequence == 0) {
// Sequence Exhausted, wait till next millisecond.
currentTimestamp = waitNextMillis(currentTimestamp);
}
} else {
// reset sequence to start with zero for the next millisecond
sequence = 0;
}
lastTimestamp = currentTimestamp;
return currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS)
| (nodeId << SEQUENCE_BITS)
| sequence;
}
// Get current timestamp in milliseconds, adjust for the custom epoch.
private long timestamp() {
return Instant.now().toEpochMilli() - customEpoch;
}
// Block and wait till next millisecond
private long waitNextMillis(long currentTimestamp) {
while (currentTimestamp == lastTimestamp) {
currentTimestamp = timestamp();
}
return currentTimestamp;
}
private long createNodeId() {
long nodeId;
try {
StringBuilder sb = new StringBuilder();
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
byte[] mac = networkInterface.getHardwareAddress();
if (mac != null) {
for(byte macPort: mac) {
sb.append(String.format("%02X", macPort));
}
}
}
nodeId = sb.toString().hashCode();
} catch (Exception ex) {
nodeId = (new SecureRandom().nextInt());
}
nodeId = nodeId & maxNodeId;
return nodeId;
}
public long[] parse(long id) {
long maskNodeId = ((1L << NODE_ID_BITS) - 1) << SEQUENCE_BITS;
long maskSequence = (1L << SEQUENCE_BITS) - 1;
long timestamp = (id >> (NODE_ID_BITS + SEQUENCE_BITS)) + customEpoch;
long nodeId = (id & maskNodeId) >> SEQUENCE_BITS;
long sequence = id & maskSequence;
return new long[]{timestamp, nodeId, sequence};
}
}
测试代码:
package com.example.orm.threads;
import com.example.orm.utils.Snowflake;
import com.example.orm.utils.SnowflakeIdWorker3rd;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.stream.Collectors;
public class MyThread extends Thread{
public static List<Long> idList = null;
public static void main(String[] args) throws InterruptedException {
idList = new ArrayList<Long>();
final CountDownLatch cdl = new CountDownLatch(1);
for (int i = 0; i < 2; i++) {
Thread t = new MyThread(cdl, i);
t.start();
}
Thread.sleep(2000);
System.out.println(idList);
System.out.println("去重前ID数量" + idList.size());
idList = idList.stream().distinct().collect(Collectors.toList());
System.out.println("去重后ID数量" + idList.size());
}
private final CountDownLatch cdl;
private final int num;
public MyThread(CountDownLatch cdl, int num) {
this.cdl = cdl;
this.num = num;
}
@Override
public void run() {
Snowflake snowflake = Snowflake.getInstance();
System.out.println("snow:" + snowflake.toString());
cdl.countDown();
try {
cdl.await();
for (int i = 0; i < 5; i++) {
long id = snowflake.nextId();
idList.add(id);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
结果:
如果不采用这种方式,多线程模式会有问题
本作品采用《CC 协议》,转载必须注明作者和本文链接
代码是不是不全也 那个CountDownLatch 和 list 没贴呀
怎样控制生成id的位数呢,我试了一都是16位的