详解Java中雪花算法的实现

yizhihongxing

详解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的项目构建工具Maven的配置和使用教程

    Java的项目构建工具Maven的配置和使用教程 简介 Maven是一款基于项目对象模型(POM)的构建工具,用于项目的构建、依赖管理和发布。本文将详细介绍如何配置和使用Maven。 安装 下载Maven。可以从官方网站https://maven.apache.org/下载最新版本。 安装Maven。 Windows:将下载的二进制文件解压至任意目录,设置系…

    Java 2023年5月20日
    00
  • Mybatis之@ResultMap,@Results,@Result注解的使用

    Mybatis是一款优秀的ORM框架,它提供了丰富的注解来进行对象和数据库的映射。其中@ResultMap、@Results、@Result三个注解是使用频率较高的几个。下面将详细讲解它们的使用方法及示例。 一、@ResultMap注解的使用 @ResultMap注解用于引用一个已经定义好的resultMap,在查询时用作查询结果集的映射。resultMap…

    Java 2023年5月20日
    00
  • struts2通过action返回json对象

    实现Struts2通过Action返回JSON对象,可以按照以下步骤进行操作: 步骤一:导入依赖 在项目的pom.xml中导入struts2-json-plugin依赖,以便能够支持JSON格式的数据: <dependency> <groupId>org.apache.struts</groupId> <artifa…

    Java 2023年5月20日
    00
  • java使用websocket,并且获取HttpSession 源码分析(推荐)

    Java使用WebSocket并获取HttpSession的攻略 WebSocket是一种双向通信协议,能够建立客户端和服务端之间的实时通信通道。本攻略将详细讲解Java如何使用WebSocket并获取HttpSession,步骤如下: 步骤1:添加依赖 在项目的pom.xml文件中添加以下依赖: <dependency> <groupId…

    Java 2023年5月23日
    00
  • 5分钟让你快速掌握java8 stream常用开发技巧

    5分钟让你快速掌握java8 stream常用开发技巧 什么是Stream Java 8引入Stream这个API是为了简化集合操作。Stream可以使用filter、map、reduce等方法对集合进行处理。在操作集合时,Stream会把操作分为中间操作和终止操作两种。中间操作用于筛选和转换数据,终止操作用来搜集数据。Stream不改变原来的集合数据,而是…

    Java 2023年5月26日
    00
  • Java 方法(方法的定义,可变参数,参数的传递问题,方法重载,方法签名)

    Java方法 Java方法是一段被命名过并执行了一些特定任务的代码。它是类的一部分,并且能够被其他方法或类通过方法调用语句来调用并执行任务。方法可以减少重复代码并有益于代码的重用。在本文中,我们将会详细讲解Java方法的定义,可变参数,参数的传递问题,方法重载和方法签名。 方法的定义 在Java中,方法定义由方法名称,返回类型,参数列表(可选)和方法体组成。…

    Java 2023年5月26日
    00
  • Spring Security权限想要细化到按钮实现示例

    为了实现Spring Security权限细化到按钮级别的权限控制,需要经过以下几个步骤: 步骤1,配置Spring Security 在Spring Security的配置中添加按钮级别的权限控制。 @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSec…

    Java 2023年5月20日
    00
  • java判断两个时间是不是同一天的方法

    判断两个时间是否为同一天,即判断它们的年、月、日是否相同,可以利用Java中的日期类来实现。下面是两种方法。 方法一:使用Calendar类 /** * 判断两个时间是否为同一天 * @param date1 时间1 * @param date2 时间2 * @return 是否为同一天 */ public static boolean isSameDay1…

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