It's a Snowflake algorithm. I implement by myself. Use generate to generate unique id.
// start time
private final long startTimestamp = ZonedDateTime.of(2015, 1, 1, 0, 0, 0, 0, ZoneId.systemDefault()).toInstant().toEpochMilli();
// shift value
private final long workerDataLeftShiftValue;
// mask
private final long sequenceMask = 4095L;
// sequence from 0 - 4095
private long sequence = 0;
// last timestamp
private long lastTimestamp = System.currentTimeMillis();
public Snowflake(int dataCenterId, int workerId) {
// shifts
int workerLeftShift = 12;
int dataCenterLeftShift = 17;
this.workerDataLeftShiftValue = workerId << workerLeftShift | dataCenterId << dataCenterLeftShift;
// max id
int maxId = 32;
if (!(dataCenterId < maxId) || !(workerId < maxId)) {
throw new IllegalStateException("not a valid id, snowflake is not working");
}
}
@Override
public synchronized long generate() {
long curTimestamp = System.currentTimeMillis();
if (lastTimestamp > curTimestamp) {
throw new IllegalStateException("last timestamp > current timestamp, clock error when using snowflake");
}
if (lastTimestamp == curTimestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
while (curTimestamp == System.currentTimeMillis()) {
curTimestamp = System.currentTimeMillis();
}
}
} else {
sequence = 0;
}
lastTimestamp = curTimestamp;
return sequence | workerDataLeftShiftValue | (curTimestamp - startTimestamp) << 22L;
}
public static void main(String[] args) {
Snowflake snowflake = new Snowflake(1, 1);
System.out.println(snowflake.generate());
}
It's a Snowflake implemented by Twitter. Use nextId to generate unique id.
private final long twepoch = 1420041600000L;
private final long workerIdBits = 5L;
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final long sequenceBits = 12L;
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
private long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public SnowflakeTwitter(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
}
if (lastTimestamp == timestamp) {
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
}
else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) //
| (datacenterId << datacenterIdShift) //
| (workerId << workerIdShift) //
| sequence;
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return System.currentTimeMillis();
}
public static void main(String[] args) {
SnowflakeTwitter idWorker = new SnowflakeTwitter(0, 0);
System.out.println(idWorker.nextId());
}
So I run some test performance on them. Use 10 threads to generate id. 1 Thread to watch the total number of id.
public static void main(String[] args) throws InterruptedException {
Snowflake snowflake = new Snowflake(1, 1);
SnowflakeTwitter snowflakeTwitter = new SnowflakeTwitter(1, 1);
AtomicInteger sf = new AtomicInteger(0);
AtomicInteger sft = new AtomicInteger(0);
int tSize = 10;
for (int i = 0; i < tSize; i++) {
new Thread(() -> {
while (true) {
snowflakeTwitter.nextId();
sft.incrementAndGet();
}
}).start();
new Thread(() -> {
while (true) {
snowflake.generate();
sf.incrementAndGet();
}
}).start();
}
new Thread(() -> {
while (true) {
System.out.println("sft: " + sft.get() + ", sf: " + sf.get());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
TimeUnit.SECONDS.sleep(100);
}
And result is: sft: 6752, sf: 5663 sft: 477792, sf: 194125 sft: 1279909, sf: 661183 sft: 2320410, sf: 1206993 sft: 3374425, sf: 1712642 sft: 4255803, sf: 2157003 sft: 4661517, sf: 2338219 sft: 5115752, sf: 2551146
So my implmentation is slow. I found the reason is: // my impl return sequence | workerDataLeftShiftValue | (curTimestamp - startTimestamp) << 22L; // twitter impl return ((timestamp - twepoch) << timestampLeftShift) // | (datacenterId << datacenterIdShift) // | (workerId << workerIdShift) // | sequence;
// my impl return sequence | workerDataLeftShiftValue | (curTimestamp - startTimestamp) << 22L; // twitter impl return ((timestamp - twepoch) << timestampLeftShift) // | (datacenterId << datacenterIdShift) // | (workerId << workerIdShift) // | sequence;
Why caused the speed difference? Thanks.
Obviously (as 1 XOR x == ~x, where ~
is the one's complement, bit negation):
-1L ^ x
is simply
~x
Then one can make the shifts use a constant, final int.
final int workerLeftShift = 12;
final int dataCenterLeftShift = 17;
I did not check the generated code, but that seems related to the expressions you mention.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.