/**
* Twitter的Snowflake 算法<br>
* 分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。<br>
* snowflake的结构如下(每部分用-分开):<br>
*
* <pre>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000
* </pre>
*
* 第一位为未使用,接下来的41位为毫秒级时间(41位的长度可以使用69年)<br>
* 然后是5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点)<br>
* 最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)<br>
*
*
*/
public class Snowflake {
private final long twepoch = 1288834974657L;
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;
private boolean useSystemClock;
/**
* 构造
* @param workerId 终端ID
* @param datacenterId 数据中心ID
*/
public Snowflake(long workerId, long datacenterId) {
this(workerId, datacenterId, false);
}
/**
* 构造
* @param workerId 终端ID
* @param datacenterId 数据中心ID
* @param isUseSystemClock 是否使用{@link SystemClock} 获取当前时间戳
*/
public Snowflake(long workerId, long datacenterId, boolean isUseSystemClock) {
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;
this.useSystemClock = isUseSystemClock;
}
/**
* 下一个ID
* @return ID
*/
public synchronized long nextId() {
long timestamp = useSystemClock ? SystemClock.now() : System.currentTimeMillis();
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;
}
private long tilNextMillis(long lastTimestamp) {
long timestamp = useSystemClock ? SystemClock.now() : System.currentTimeMillis();
while (timestamp <= lastTimestamp) {
timestamp = useSystemClock ? SystemClock.now() : System.currentTimeMillis();
}
return timestamp;
}
}
import java.sql.Timestamp;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
/**
* 系统时钟
* 高并发场景下System.currentTimeMillis()的性能问题的优化
* System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右)
* System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道
* 后台定时更新时钟,JVM退出时,线程自动回收
*
*/
public class SystemClock {
/** 时钟更新间隔,单位毫秒 */
private final long period;
/** 现在时刻的毫秒数 */
private volatile long now;
/**
* 构造
* @param period
*/
private SystemClock(long period) {
this.period = period;
this.now = System.currentTimeMillis();
scheduleClockUpdating();
}
/**
* 开启计时器线程
*/
private void scheduleClockUpdating() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory(){
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "System Clock");
thread.setDaemon(true);
return thread;
}
});
scheduler.scheduleAtFixedRate(new Runnable(){
@Override
public void run() {
now = System.currentTimeMillis();
}
}, period, period, TimeUnit.MILLISECONDS);
}
/**
* @return 当前时间毫秒数
*/
private long currentTimeMillis() {
return now;
}
//------------------------------------------------------------------------ static
/**
* 单例
*
*/
private static class InstanceHolder {
public static final SystemClock INSTANCE = new SystemClock(1);
}
/**
* 单例实例
* @return 单例实例
*/
private static SystemClock instance() {
return InstanceHolder.INSTANCE;
}
/**
* @return 当前时间
*/
public static long now() {
return instance().currentTimeMillis();
}
/**
* @return 当前时间字符串表现形式
*/
public static String nowDate() {
return new Timestamp(instance().currentTimeMillis()).toString();
}
}
主要适合于生成全局唯一ID,大家可以试试。