详解Java中雪花算法的实现

详解Java中雪花算法的实现

需求概述

在分布式系统中,为了保证业务数据的唯一性,需要生成唯一的ID。传统的ID生成方式可能出现因为高并发而重复的情况,而雪花算法(Snowflake)正是为了解决这个问题而出现的。

本文会详细介绍Java中雪花算法的实现,及其原理。

雪花算法的基本原理

雪花算法是Twitter开源的分布式ID生成算法,采用一个64位的long类型作为唯一ID,其中各个部分表示的含义如下:

  1. 1位不用,可以用作别的标识
  2. 41位表示毫秒数,可以使用69年,到最高位是2199023255551(因为使用long存储,所以需要使用L作为后缀)
  3. 10位表示机器的id(5位机房id + 5位机器id),可以使用1024台机器
  4. 12位表示某一毫秒内的序列号,同一毫秒内最多可以生成4096个ID

Java中雪花算法的实现

下面是Java中实现雪花算法的代码示例:

public class SnowflakeIdGenerator {

    /**
     * 雪花算法的起始时间,用于计算时间戳
     */
    private static final long START_TIME = 1609459200000L;

    private static final long WORKER_ID_BITS = 5L;
    private static final long DATACENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);

    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; 

    /**
     * 工作节点ID
     */
    private final long workerId;

    /**
     * 数据中心ID
     */
    private final long datacenterId;

    /**
     * 当前毫秒生成的序列号
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(long workerId, long datacenterId) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
        }

        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", MAX_DATACENTER_ID));
        }

        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 生成一个唯一ID
     *
     * @return 雪花算法生成的唯一ID
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");
        }

        if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & ~(-1L << SEQUENCE_BITS);
            if (sequence == 0L) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        return ((timestamp - START_TIME) << TIMESTAMP_LEFT_SHIFT)
                | (datacenterId << DATACENTER_ID_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }

    /**
     * 获取当前的时间戳,并减去起始时间,保证不同数据中心和机器的时间戳序列递增
     */
    private long timeGen() {
        return System.currentTimeMillis() - START_TIME;
    }

    /**
     * 如果当前时间戳与上次生成的时间戳相同,则等待下一毫秒再生成
     */
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }
}

雪花算法生成ID的过程中,通过timeGen()方法获取当前的时间戳,并减去起始时间。通过&运算保证序列号不溢出。

示例说明

下面分别是两个雪花算法的生成ID的例子:

public class SnowflakeIdGeneratorTest {

    /**
     * 单机生成ID的示例
     */
    @Test
    public void testSingleMachine() {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1);
        System.out.println(idGenerator.nextId());
    }

    /**
     * 多机房、多机器生成ID的示例
     */
    @Test
    public void testMultiMachine() {
        SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(15, 64);
        System.out.println(idGenerator.nextId());
    }
}

其中testSingleMachine()方法是在单机器上生成ID的示例,testMultiMachine()方法是在多机器、多机房的情况下生成ID的示例。

总结

通过本文的介绍,我们了解到了雪花算法的基本原理和Java中的实现方式,同时还提供了多机房、多机器和单机器模式下的示例。

在实际开发中,我们可以根据具体的业务需求,选择合适的方式生成唯一ID,保证业务数据的唯一性。

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

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

相关文章

  • Java之使用POI教你玩转Excel导入与导出

    Java之使用POI教你玩转Excel导入与导出 什么是POI POI是一个Java开发的用于操作Microsoft Office格式文件的开源框架。POI可以读写文档、演示文稿、Excel文件等,并且支持多种文件格式。下面我们主要讲解POI在Java中如何操作Excel文件的导入与导出。 Excel文件的导入 准备工作 首先,我们需要在pom.xml文件中…

    Java 2023年5月19日
    00
  • java可变参数当做数组处理的方法示例

    Java的可变参数可以让我们在定义方法时不确定参数的个数,这些参数被当做数组来处理,能够使方法的调用更加灵活方便。下面将为大家介绍Java可变参数当做数组处理的方法示例,具体步骤如下: 第一步:定义一个接收可变参数的方法 首先,我们需要在Java代码中定义一个接收可变参数的方法。以打印数组中所有元素为例,代码如下: public static void pr…

    Java 2023年5月26日
    00
  • linux安装RocketMQ实例步骤

    下面是“Linux安装RocketMQ实例步骤”的完整攻略。 准备工作 在进行安装前,请确保你已经完成如下步骤: 安装好Java环境(建议使用JDK 8及以上版本)。 确认安装好了RocketMQ服务端的压缩包(下载地址详见官网)。 确认你拥有安装并运行RocketMQ所需的系统权限。 安装步骤 下载RocketMQ服务端的压缩包,解压到指定目录下: bas…

    Java 2023年6月2日
    00
  • IE cache缓存 所带来的问题收藏

    IE cache缓存所带来的问题 什么是IE cache缓存? IE cache缓存是指Internet Explorer浏览器在浏览网页过程中自动缓存的网页文件,包括了html网页文件、js和css等其他资源文件,以让用户在下次访问同一网页时更快地加载页面和资源,提升用户体验。 IE cache缓存带来的问题 尽管IE cache缓存可以提升用户体验,但是…

    Java 2023年6月15日
    00
  • Spring整合Struts2的两种方法小结

    下面我将详细讲解“Spring整合Struts2的两种方法小结”的完整攻略。 什么是Spring整合Struts2 Spring整合Struts2指的是将Struts2框架和Spring框架进行整合,使两者能够协同工作,共同完成一个Web应用的构建。这种整合方式有利于提高应用的开发效率和可维护性。 方法一:基于Struts2的Action实现Spring B…

    Java 2023年5月20日
    00
  • 布隆过滤器(Bloom Filter)的Java实现方法

    布隆过滤器(Bloom Filter)的Java实现方法 什么是布隆过滤器? 布隆过滤器(Bloom Filter)是一种数据结构,它可以用来判断一个元素是否可能存在于一个集合中,但并不能确定该元素是否一定存在于该集合中。因为该数据结构的判断结果在误判率(False Positive Rate)上具有一定的不确定性。布隆过滤器可以在空间和时间上做到非常高效,…

    Java 2023年5月26日
    00
  • 关于Jedis的用法以及Jedis使用Redis事务

    关于Jedis的用法以及使用Jedis执行Redis事务的攻略如下: Jedis 的用法 Jedis 是 Redis 的一个 Java 客户端库,用于在 Java 应用程序中与 Redis 进行交互。使用 Jedis 需要先引入 Jedis 的依赖,例如在 Maven 项目中,需要在 pom.xml 文件中加入以下依赖: <dependency>…

    Java 2023年5月20日
    00
  • javascript:void(0)是什么意思及href=#与href=javascriptvoid(0)的区别

    JavaScript是一种基于事件驱动的编程语言,常在网页中使用。在网页开发中,常见的有一种a标签的href属性值为javascript:void(0)的情况,也有href属性值为#的情况。这两种情况看起来很相似,但实际上却有很大的区别。 javascript:void(0)是什么? javascript:void(0)表示在当前网页执行一段JavaScri…

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