SnowFlake 生成全局唯一id
public class SnowFlakeUtil {
    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long twepoch = 1288834974657L; // Thu, 04 Nov 2010 01:42:54 GMT 标记时间 用来计算偏移量,距离当前时间不同,得到的数据的位数也不同
    private long workerIdBits = 5L; // 物理节点ID长度
    private long datacenterIdBits = 5L; // 数据中心ID长度
    private long maxWorkerId = -1L ^ (-1L << workerIdBits); // 最大支持机器节点数0~31,一共32个
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); // 最大支持数据中心节点数0~31,一共32个
    private long sequenceBits = 12L; // 序列号12位, 4095,同毫秒内生成不同id的最大个数
    private long workerIdShift = sequenceBits; // 机器节点左移12位
    private long datacenterIdShift = sequenceBits + workerIdBits; // 数据中心节点左移17位
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; // 时间毫秒数左移22位
    private long sequenceMask = -1L ^ (-1L << sequenceBits); // 用于和当前时间戳做比较,以获取最新时间
    private long lastTimestamp = -1L;
    /**
     * 成员类,SnowFlakeUtil的实例对象的保存域
     *
     */
    private static class IdGenHolder {
        private static final SnowFlakeUtil instance = new SnowFlakeUtil();
    }
    /**
     * 外部调用获取SnowFlakeUtil的实例对象,确保不可变
     *
     * @return
     */
    public static SnowFlakeUtil get() {
        return IdGenHolder.instance;
    }
    /**
     * 初始化构造,无参构造有参函数,默认节点都是0
     */
    public SnowFlakeUtil() {
        this(0L, 0L);
    }
    /**
     * 设置机器节点和数据中心节点数,都是 0-31
     *
     * @param workerId
     * @param datacenterId
     */
    public SnowFlakeUtil(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;
    }
    /**
     * 线程安全的id生成方法
     *
     * @return
     */
    @SuppressWarnings("all")
    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只有12bit,所以和sequenceMask相与一下,去掉高位
            sequence = (sequence + 1) & sequenceMask;
            // 判断是否溢出,也就是每毫秒内超过4095,当为4096时,与sequenceMask相与,sequence就等于0
            if (sequence == 0) {
                // 自旋等待到下一毫秒
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加,每个毫秒时间内,都是从0开始计数,最大4095
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        // 最后按照规则拼出ID 64位
        // 000000000000000000000000000000000000000000 00000 00000 000000000000
        // 1位固定整数 time datacenterId workerId sequence
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;
    }
    /**
     * 比较当前时间和过去时间,防止时钟回退(机器问题),保证给的都是最新时间/最大时间
     *
     * @param lastTimestamp
     * @return
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
    /**
     * 获取当前的时间戳(毫秒)
     *
     * @return
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }
    /**
     * 获取全局唯一id
     */
    public static String getId() {
        Long id = SnowFlakeUtil.get().nextId();
        return id.toString();
    }
}
SnowFlake 生成全局唯一id的更多相关文章
- 如何在高并发分布式系统中生成全局唯一Id
		月整理出来,有兴趣的园友可以关注下我的博客. 分享原由,最近公司用到,并且在找最合适的方案,希望大家多参与讨论和提出新方案.我和我的小伙伴们也讨论了这个主题,我受益匪浅啊…… 博文示例: 1. ... 
- 常见的生成全局唯一id有哪些?他们各有什么优缺点?
		分布式系统中全局唯一id是我们经常用到的,生成全局id方法由很多,我们选择的时候也比较纠结.每种方式都有各自的使用场景,如果我们熟悉各种方式及优缺点,使用的时候才会更方便.下面我们就一起来看一下常见的 ... 
- 面试官:如何在分布式场景下生成全局唯一 ID?
		在分布式系统中,有一些场景需要使用全局唯一 ID ,可以和业务场景有关,比如支付流水号,也可以和业务场景无关,比如分库分表后需要有一个全局唯一 ID,或者用作事务版本号.分布式链路追踪等等,好的全局唯 ... 
- 如何在高并发分布式系统中生成全局唯一Id(转)
		http://www.cnblogs.com/heyuquan/p/global-guid-identity-maxId.html 又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文, ... 
- (转)如何在高并发分布式系统中生成全局唯一Id
		又一个多月没冒泡了,其实最近学了些东西,但是没有安排时间整理成博文,后续再奉上.最近还写了一个发邮件的组件以及性能测试请看 <NET开发邮件发送功能的全面教程(含邮件组件源码)> ,还弄了 ... 
- 生成全局唯一ID
		在实际业务处理中,有时需要生成全局唯一ID来区别同类型的不同事物,介绍一下几种方式及其C++实现 //获取全局唯一ID //server_id为服务的id,因当同一个服务部署在多个服务器上时,需要区别 ... 
- 高并发分布式系统中生成全局唯一Id汇总
		数据在分片时,典型的是分库分表,就有一个全局ID生成的问题.单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障. 2 以时间为序,或者ID里包含时间 ... 
- 游戏服务器生成全局唯一ID的几种方法
		在服务器系统开发时,为了适应数据大并发的请求,我们往往需要对数据进行异步存储,特别是在做分布式系统时,这个时候就不能等待插入数据库返回了取自动id了,而是需要在插入数据库之前生成一个全局的唯一id,使 ... 
- 雪花算法生成全局唯一ID
		系统中某些场景少不了全局唯一ID的使用,来保证数据的唯一性.除了通过数据库自带的自增id来保证 id 的唯一性,通常为了保证的数据的可移植性会选择通过程序生成全局唯一 id.百度了不少php相关的生成 ... 
随机推荐
- kaptcha验证码组件使用简介
			Kaptcha是一个基于SimpleCaptcha的验证码开源项目. 官网地址:http://code.google.com/p/kaptcha/ kaptcha的使用比较方便,只需添加jar包依赖之 ... 
- 服务器意外重启导致storm报错的问题处理
			解决方法 cat /opt/storm-0.8.2/conf/storm.yaml中找到storm.local.dir设定的目录,备份supervisor和workers两个文件夹,#nohup su ... 
- axis客户端循环访问webservice的时候只发送了几条数据就断开了的问题
			原因 axis客户端访问webservice默认是使用http1.0版本的,这个版本的http不能保持长连接,应该换成http1.1版本 具体修改步骤: <?xml version=" ... 
- boxing & unboxing
			[boxing & unboxing] Boxing is the process of converting a value type to the type object or to an ... 
- Excel VBA入门(四)流程控制2-循环控制
			所谓循环控制,即在循环执行一段代码,用于完成一些重复性任务. VBA中的循环控制语句主要有3种:for.while.loop.对于大多数人来说,for的使用频率最高,而我个人也觉得for是最为灵活的, ... 
- VUE,使用物理引擎Box2D设计类愤怒小鸟的击球游戏--基本架构设置
- java命令查询属性信息
			System.getProperty("user.home")可以查询JAVA系统的user.home属性的值, 除了user.home,还有user.dir, file.sepa ... 
- Java中文乱码解决方案
			Java中文乱码解决方案 1.中文乱码解决方案,确保每个文件的默认编码是UTF-8 加入 URIEncoding="UTF-8" 代码中的设置 1>在se ... 
- SpringBoot的配置属性文件*.properties值如何映射到类中使用
			想要在JAVA Bean中读取配置文件中的内容有两种方式,可以进行获取到 第一种方式: 1.在默认的配置文件application.properties 中进行设置 Key-Value键值对 com. ... 
- code3731 寻找道路
			将图反向,从终点spfa,把和终点没有联系的点找出来, 把它们和它们所连接的所有点删去(反向图) 然后再一遍spfa,输出最短路即可 代码: #include<iostream> #inc ... 
