个人随笔
目录
工具类:Java版本实现SnowflakeIdUtil雪花算法
2020-09-11 23:04:19

有时候我们需要生成一批自增且唯一的ID,UUID可以保证唯一性,但是没有顺序,因此这里选择雪花算法,只要时间不会播,理论上不会重复,当然必须用单线程来跑:

  1. public class SnowflakeIdUtil {
  2. // ==============================Fields===========================================
  3. /** 开始时间截 (2015-01-01) */
  4. private final long twepoch = 1420041600000L;
  5. /** 机器id所占的位数 */
  6. private final long workerIdBits = 5L;
  7. /** 数据标识id所占的位数 */
  8. private final long datacenterIdBits = 5L;
  9. /** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */
  10. private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
  11. /** 支持的最大数据标识id,结果是31 */
  12. private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
  13. /** 序列在id中占的位数 */
  14. private final long sequenceBits = 12L;
  15. /** 机器ID向左移12位 */
  16. private final long workerIdShift = sequenceBits;
  17. /** 数据标识id向左移17位(12+5) */
  18. private final long datacenterIdShift = sequenceBits + workerIdBits;
  19. /** 时间截向左移22位(5+5+12) */
  20. private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
  21. /** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */
  22. private final long sequenceMask = -1L ^ (-1L << sequenceBits);
  23. /** 工作机器ID(0~31) */
  24. private long workerId;
  25. /** 数据中心ID(0~31) */
  26. private long datacenterId;
  27. /** 毫秒内序列(0~4095) */
  28. private long sequence = 0L;
  29. /** 上次生成ID的时间截 */
  30. private long lastTimestamp = -1L;
  31. private static SnowflakeIdUtil idWorker;
  32. /**
  33. * 以SnowFlake算法,获取唯一有序id
  34. * @return
  35. */
  36. public static long getSnowflakeId() {
  37. if(idWorker == null) {
  38. synchronized (SnowflakeIdUtil.class) {
  39. if(idWorker == null) {
  40. idWorker = new SnowflakeIdUtil(0, 0);
  41. }
  42. }
  43. }
  44. return idWorker.nextId();
  45. }
  46. // ==============================Methods==========================================
  47. private SnowflakeIdUtil() {
  48. }
  49. //==============================Constructors=====================================
  50. /**
  51. * 构造函数
  52. * @param workerId 工作ID (0~31)
  53. * @param datacenterId 数据中心ID (0~31)
  54. */
  55. private SnowflakeIdUtil(long workerId, long datacenterId) {
  56. if (workerId > maxWorkerId || workerId < 0) {
  57. throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
  58. }
  59. if (datacenterId > maxDatacenterId || datacenterId < 0) {
  60. throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
  61. }
  62. this.workerId = workerId;
  63. this.datacenterId = datacenterId;
  64. }
  65. /**
  66. * 阻塞到下一个毫秒,直到获得新的时间戳
  67. * @param lastTimestamp 上次生成ID的时间截
  68. * @return 当前时间戳
  69. */
  70. protected long tilNextMillis(long lastTimestamp) {
  71. long timestamp = timeGen();
  72. while (timestamp <= lastTimestamp) {
  73. timestamp = timeGen();
  74. }
  75. return timestamp;
  76. }
  77. /**
  78. * 返回以毫秒为单位的当前时间
  79. * @return 当前时间(毫秒)
  80. */
  81. protected long timeGen() {
  82. return System.currentTimeMillis();
  83. }
  84. /**
  85. * 获得下一个ID (该方法是线程安全的)
  86. * @return SnowflakeId
  87. */
  88. protected synchronized long nextId() {
  89. long timestamp = timeGen();
  90. //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
  91. if (timestamp < lastTimestamp) {
  92. throw new RuntimeException(
  93. String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
  94. }
  95. //如果是同一时间生成的,则进行毫秒内序列
  96. if (lastTimestamp == timestamp) {
  97. sequence = (sequence + 1) & sequenceMask;
  98. //毫秒内序列溢出
  99. if (sequence == 0) {
  100. //阻塞到下一个毫秒,获得新的时间戳
  101. timestamp = tilNextMillis(lastTimestamp);
  102. }
  103. }
  104. //时间戳改变,毫秒内序列重置
  105. else {
  106. sequence = 0L;
  107. }
  108. //上次生成ID的时间截
  109. lastTimestamp = timestamp;
  110. //移位并通过或运算拼到一起组成64位的ID
  111. return ((timestamp - twepoch) << timestampLeftShift) //
  112. | (datacenterId << datacenterIdShift) //
  113. | (workerId << workerIdShift) //
  114. | sequence;
  115. }
  116. public static void main(String[] args) {
  117. long snowflakeId = getSnowflakeId();
  118. System.out.println(snowflakeId);
  119. }
  120. }
 5018

啊!这个可能是世界上最丑的留言输入框功能~


当然,也是最丑的留言列表

有疑问发邮件到 : suibibk@qq.com 侵权立删
Copyright : 个人随笔   备案号 : 粤ICP备18099399号-2