SnowFlake算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package util;
 
 
public class IdUtils {
 
    private static IdWorkerUtils idWorkerUtils=new IdWorkerUtils(1,1,1);
    public static Long nextId(){
       return idWorkerUtils.nextId();
    }
 
    private static class IdWorkerUtils {
 
        //下面两个每个5位,加起来就是10位的工作机器id
        private long workerId;    //工作id
        private long datacenterId;   //数据id
        //12位的序列号
        private long sequence;
 
        public IdWorkerUtils(long workerId, long datacenterId, long sequence){
            // sanity check for workerId
            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));
            }
            System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
                    timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
 
            this.workerId = workerId;
            this.datacenterId = datacenterId;
            this.sequence = sequence;
        }
 
        //初始时间戳
        private long twepoch = 1288834974657L;
 
        //长度为5位
        private long workerIdBits = 5L;
        private long datacenterIdBits = 5L;
        //最大值
        private long maxWorkerId = -1L ^ (-1L << workerIdBits);
        private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
        //序列号id长度
        private long sequenceBits = 12L;
        //序列号最大值
        private long sequenceMask = -1L ^ (-1L << sequenceBits);
 
        //工作id需要左移的位数,12位
        private long workerIdShift = sequenceBits;
        //数据id需要左移位数 12+5=17位
        private long datacenterIdShift = sequenceBits + workerIdBits;
        //时间戳需要左移位数 12+5+5=22位
        private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
 
        //上次时间戳,初始值为负数
        private long lastTimestamp = -1L;
 
        public long getWorkerId(){
            return workerId;
        }
 
        public long getDatacenterId(){
            return datacenterId;
        }
 
        public long getTimestamp(){
            return System.currentTimeMillis();
        }
 
        //下一个ID生成算法
        public synchronized long nextId() {
            long timestamp = timeGen();
 
            //获取当前时间戳如果小于上次时间戳,则表示时间戳获取出现异常
            if (timestamp < lastTimestamp) {
                System.err.printf("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp);
                throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds",
                        lastTimestamp - timestamp));
            }
 
            //获取当前时间戳如果等于上次时间戳(同一毫秒内),则在序列号加一;否则序列号赋值为0,从0开始。
            if (lastTimestamp == timestamp) {
                sequence = (sequence + 1) & sequenceMask;
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0;
            }
 
            //将上次时间戳值刷新
            lastTimestamp = timestamp;
 
            /**
             * 返回结果:
             * (timestamp - twepoch) << timestampLeftShift) 表示将时间戳减去初始时间戳,再左移相应位数
             * (datacenterId << datacenterIdShift) 表示将数据id左移相应位数
             * (workerId << workerIdShift) 表示将工作id左移相应位数
             * | 是按位或运算符,例如:x | y,只有当x,y都为0的时候结果才为0,其它情况结果都为1。
             * 因为个部分只有相应位上的值有意义,其它位上都是0,所以将各部分的值进行 | 运算就能得到最终拼接好的id
             */
            return ((timestamp - twepoch) << timestampLeftShift) |
                    (datacenterId << datacenterIdShift) |
                    (workerId << workerIdShift) |
                    sequence;
        }
 
        //获取时间戳,并与上次时间戳比较
        private long tilNextMillis(long lastTimestamp) {
            long timestamp = timeGen();
            while (timestamp <= lastTimestamp) {
                timestamp = timeGen();
            }
            return timestamp;
        }
 
        //获取系统时间戳
        private long timeGen(){
            return System.currentTimeMillis();
        }
 
        //---------------测试---------------
        public static void main(String[] args) {
            com.longfor.falcon.util.IdWorkerUtils worker = new com.longfor.falcon.util.IdWorkerUtils(1,1,1);
            for (int i = 0; i < 30; i++) {
                System.out.println(worker.nextId());
            }
        }
 
    }
 
}

 

posted @   甜菜波波  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
历史上的今天:
2018-08-01 jmap 查看内存使用直方图
2018-08-01 并发下线程池的最佳数量计算
2018-08-01 java中的类加载器ClassLoader和类初始化
2017-08-01 c# CacheManager 缓存管理
2017-08-01 新一代AJAX API:FETCH
2017-08-01 SqlServer,Oracle,Mysql 获取指定行数
2017-08-01 事件总线EventBus
点击右上角即可分享
微信分享提示