生成唯一ID

/**
 * 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,大家可以试试。

猜你喜欢

转载自blog.csdn.net/u010138906/article/details/78809321