Java实现雪花算法的示例代码

题目:Java实现雪花算法的示例代码

1. 什么是雪花算法?

雪花算法(Snowflake)是Twitter公司开发的一种唯一ID生成算法,它可以生成一个长度为64bit的唯一ID,被广泛应用于分布式系统中,这样可以避免ID冲突的情况。

雪花算法的生成,主要依靠了数据中心ID(5位)、机器ID(5位)、时间戳(41位)以及自增的序列(12位)。

2. 雪花算法的实现

在Java中,我们通常是通过long类型来表示一个唯一ID的,因为long类型有64位,正好可以存储一个雪花算法生成的唯一ID。下面是Java实现雪花算法的示例代码:

public class SnowflakeIdWorker {

    // 数据中心ID的长度
    private static final long DATA_CENTER_ID_BITS = 5L;
    // 机器ID的长度
    private static final long WORKER_ID_BITS = 5L;
    // 序列号的长度
    private static final long SEQUENCE_BITS = 12L;

    // 最大支持的数据中心ID,结果是31(2的5次方减1)
    private static final long MAX_DATA_CENTER_ID = ~(-1L << DATA_CENTER_ID_BITS);
    // 最大支持的机器ID,结果是31(2的5次方减1)
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    // 最大支持的序列号,结果是4095(2的12次方减1)
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    // 机器ID和数据中心ID的偏移量,结果是5(序列号长度+机器ID长度)
    private static final long WORKER_ID_LEFT_SHIFT = SEQUENCE_BITS;
    private static final long DATA_CENTER_ID_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;

    // 数据中心ID,默认是0
    private long dataCenterId = 0L;
    // 机器ID,默认是0
    private long workerId = 0L;
    // 序列号,初始值为0
    private long sequence = 0L;
    // 上一次生成ID的时间戳
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     * @param dataCenterId 数据中心ID
     * @param workerId 机器ID
     */
    public SnowflakeIdWorker(long dataCenterId, long workerId) {
        if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
            throw new IllegalArgumentException("Data center ID can't be greater than " + MAX_DATA_CENTER_ID + " or less than 0");
        }
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException("Worker ID can't be greater than " + MAX_WORKER_ID + " or less than 0");
        }
        this.dataCenterId = dataCenterId;
        this.workerId = workerId;
    }

    /**
     * 生成雪花ID
     * @return
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards, Refusing to generate id");
        }
        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            if (sequence == 0L) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = timestamp;
        return ((timestamp - 1288834974657L) << TIMESTAMP_LEFT_SHIFT) | (dataCenterId << DATA_CENTER_ID_LEFT_SHIFT) | (workerId << WORKER_ID_LEFT_SHIFT) | sequence;
    }

    /**
     * 阻塞到下一个毫秒,直到获得新的时间戳
     * @param lastTimestamp 上一次生成ID的时间戳
     * @return 当前时间戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}

在上面的示例代码中,我们通过一个叫做SnowflakeIdWorker的类来实现了雪花算法。在这个类中,重点是实现nextId()方法:

  1. 获取当前时间戳;
  2. 判断时间戳是否小于上一次生成ID的时间戳,如果小于则抛出异常;
  3. 如果时间戳与上一次生成ID的时间戳相等,则自增序列号;
  4. 如果序列号达到最大值,在下一个时间戳生成;
  5. 将唯一ID的各部分组合起来。

其中,我们使用synchronized修饰了nextId()方法,保证了线程安全性。

3. 示例说明

示例一:生成10个唯一ID

// 创建两个雪花ID生成器
SnowflakeIdWorker worker1 = new SnowflakeIdWorker(1, 1);
SnowflakeIdWorker worker2 = new SnowflakeIdWorker(1, 2);

// 生成10个唯一ID
for (int i = 0; i < 10; i++) {
    System.out.println("Worker1: " + worker1.nextId());
    System.out.println("Worker2: " + worker2.nextId());
}

在这个示例中,我们创建了两个雪花ID生成器,分别设置了不同的数据中心ID和机器ID。然后,我们循环生成10个唯一ID,并分别输出它们对应的生成器信息,从而验证它们是唯一的。

示例二:生成一个唯一ID的时间测试

// 创建雪花ID生成器
SnowflakeIdWorker worker = new SnowflakeIdWorker(1, 1);

// 输出当前时间戳
System.out.println("Current TimeStamp: " + System.currentTimeMillis());

// 生成一个唯一ID
long id = worker.nextId();
System.out.println("Unique ID: " + id);

// 输出生成唯一ID后的时间戳
System.out.println("TimeStamp After Generate Unique ID: " + (id >> 22) + 1288834974657L);

这个示例中,我们创建一个雪花ID生成器,并输出当前时间戳的值,然后生成一个唯一ID,并输出唯一ID的值和生成唯一ID后的时间戳的值。通过这种方式,我们可以很容易地验证唯一ID的生成时间。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java实现雪花算法的示例代码 - Python技术站

(0)
上一篇 2023年5月18日
下一篇 2023年5月18日

相关文章

  • JDO 2.0查询语言的特点

    JDO(Java Data Object) 是一种面向对象的数据持久化框架,它提供了一个标准API来使得Java应用程序与关系型数据库交互。JDO 2.0版本提供了一个强大的查询语言,下面我们详细讲解其特点。 JDO 2.0查询语言的特点: 1. 支持“from”语法 查询语句以“from”关键词开始,后面接一个或多个表名或实体类名。示例: SELECT e…

    Java 2023年6月15日
    00
  • Spring SpringMVC在启动完成后执行方法源码解析

    在Spring和SpringMVC中,我们可以在启动完成后执行一些方法。本文将详细讲解Spring和SpringMVC在启动完成后执行方法的源码解析,并提供两个示例说明。 Spring中启动完成后执行方法 在Spring中,我们可以使用ApplicationListener接口来监听ApplicationContext的启动事件。下面是一个示例: @Comp…

    Java 2023年5月18日
    00
  • Springboot 全局时间格式化操作

    下面是关于Spring Boot全局时间格式化操作的完整攻略。 背景 Spring Boot是一个使用很方便的轻量级框架,它内置了很多常用的扩展功能。在实际应用中,我们经常需要对时间类型数据进行格式化处理,以满足业务需求。此时,全局时间格式化就成了必不可少的一个功能。 解决方案 方案一:在全局配置文件中配置时间格式 可以在application.proper…

    Java 2023年5月20日
    00
  • Java递归算法详解(动力节点整理)

    Java递归算法详解(动力节点整理) 什么是递归? 递归是指在函数的定义中,直接或间接地调用自身的行为。 递归调用的实现过程 递归调用是通过栈实现的,每一次函数调用会将调用时的参数和函数运行的状态信息压入栈中,函数运行完成后,再从栈中弹出上一次调用的信息并恢复上一种状态信息,继续执行下去。 递归调用的分类 递归调用可以分为两类:直接递归和间接递归。 直接递归…

    Java 2023年5月26日
    00
  • java如何实现自动生成数据库设计文档

    实现Java自动生成数据库设计文档的过程可以分为以下几个步骤: 获取数据库的基本信息 首先需要连接到数据库,获取其中的基本信息,例如数据库的名称、版本号等。在Java中可以使用JDBC连接数据库,通过执行SQL语句获取这些信息。 获取数据库中的表信息 获取数据库中的表信息,包括表名、表的列信息等。可以通过执行SQL语句查询system表或metadata元数…

    Java 2023年5月19日
    00
  • java线程池参数位置导致的夺命故障宿主机打不开

    线程池是一种常见的并发处理机制,它可以有效地管理线程的生命周期,避免频繁创建和销毁线程而导致系统开销过大的问题。不过,在进行线程池的使用时,需要设置相应的参数,否则可能会导致不可预料的问题。 下面是针对“java线程池参数位置导致的夺命故障宿主机打不开”的攻略,具体内容如下: 1. 背景介绍 在使用线程池时,常见的参数包括线程池大小、任务队列大小、线程空闲时…

    Java 2023年5月27日
    00
  • Spring Boot详解创建和运行基础流程

    下面是关于”Spring Boot详解创建和运行基础流程”的完整攻略。 简介 Spring Boot是一个快速开发框架,它旨在使开发人员快速轻松地构建基于Spring框架的应用程序。它通过自动化常见的应用程序配置和通用开发任务来减少开发人员的工作量,并使他们能够专注于业务逻辑。 在本文中,我们将学习如何创建和运行基于Spring Boot的应用程序,并创建一…

    Java 2023年5月15日
    00
  • Java处理图片实现base64编码转换

    Java处理图片实现base64编码转换,可以分为以下步骤: 读取图片文件并进行Base64编码 Base64编码后的字符串可以进行传输、存储等操作 将Base64字符串解码为字节数组,并转换为图片文件保存到本地 具体步骤及示例代码如下: 1. 读取图片文件并进行Base64编码 import java.io.FileInputStream; import …

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部