前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java如何实现生成永不重复的数字方案解读!

Java如何实现生成永不重复的数字方案解读!

原创
作者头像
喵手
发布2024-12-30 00:16:52
发布2024-12-30 00:16:52
3220
举报
文章被收录于专栏:Java进阶实战Java进阶实战

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云/阿里云/华为云/51CTO;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

在上一期的文章中,我们深入探讨了如何通过 Java 反射机制 来创建静态和非静态内部类,解决了动态类实例化中的一些常见难题。通过反射的灵活操作,我们能够在运行时动态生成对象并调用它们的相关方法,极大地提升了程序的扩展性与灵活性。然而,在现代应用开发中,另一个常见的需求是生成 永不重复的数字。无论是在订单系统中生成唯一订单号,还是分布式系统中生成唯一标识,生成不重复的数字或ID都是至关重要的。

本期我们将关注 Java 生产永不重复的数字,通过多个角度剖析不同场景下的解决方案,结合具体的代码和实际应用场景,帮助开发者选择适合的实现方式。

摘要

本文以 Java 实现生成永不重复的数字 为核心,详细介绍了几种不同的实现方法,包括简单的自增算法、基于时间戳的生成方式、UUID 的使用,以及在分布式系统中常见的雪花算法。每种方法都有其适用的场景和优势。通过源码解析、实际使用案例分享和测试用例,我们将探讨如何在不同场景下生成唯一且不重复的数字或标识符,并分析各方法的优缺点,帮助开发者选择适合自己业务的最佳方案。

概述

在现代应用中,生成唯一且不重复的数字是一项关键任务,尤其是在分布式系统和多线程环境中。例如:

  • 电商系统中生成唯一订单号
  • 社交网络中为用户生成唯一的ID
  • 分布式数据库中生成唯一的主键

常见的生成方式

  1. 自增数字:最简单的生成唯一数字的方式,即通过一个全局递增的数字生成器。
  2. 时间戳结合随机数:通过系统当前时间(时间戳)加上随机数来生成不重复的数字。
  3. UUID:Java 自带的 UUID 类,能够生成几乎保证全局唯一的标识符。
  4. 雪花算法(Snowflake):Twitter 提出的分布式系统中生成全局唯一ID的算法。

每种方式都有不同的使用场景,我们将逐一分析。

源码解析

1. 自增数字生成器

最简单的方式是使用自增数字,通过维护一个全局变量,每次生成一个数字时,将其自增。对于单线程环境或简单的需求场景,这种方式非常有效。

代码语言:java
复制
public class IncrementalNumberGenerator {
    private static long currentNumber = 0;

    // 线程安全的自增方法
    public static synchronized long getNextNumber() {
        return ++currentNumber;
    }
}
代码解析:
  • currentNumber 作为静态变量,存储当前的数字。
  • getNextNumber 方法使用 synchronized 关键字确保线程安全,在并发环境下防止多线程同时修改 currentNumber 的问题。

2. 时间戳结合随机数生成

时间戳(毫秒级)结合随机数生成唯一数字的方式较为常见,能够在较大范围内保证唯一性。

代码语言:java
复制
import java.util.Random;

public class TimestampRandomNumberGenerator {
    private static final Random random = new Random();

    public static String generateUniqueNumber() {
        long timestamp = System.currentTimeMillis();
        int randomNumber = random.nextInt(1000); // 随机生成0-999的数字
        return timestamp + String.format("%03d", randomNumber); // 拼接时间戳和随机数
    }
}
代码解析:
  • System.currentTimeMillis() 获取当前时间戳(单位:毫秒)。
  • 使用 Random 类生成一个三位随机数。
  • 将时间戳和随机数拼接成一个字符串,保证唯一性。

3. UUID 生成

UUID(Universally Unique Identifier) 是一种128位的全局唯一标识符,Java 中的 java.util.UUID 类能够直接生成不重复的 UUID。

代码语言:java
复制
import java.util.UUID;

public class UUIDGenerator {
    public static String generateUUID() {
        return UUID.randomUUID().toString();
    }
}
代码解析:
  • UUID.randomUUID() 生成一个随机的 UUID。
  • UUID 通常由32个字符组成,包含字母和数字,格式如 550e8400-e29b-41d4-a716-446655440000

4. 雪花算法(Snowflake)

雪花算法是一种分布式环境下生成唯一ID的算法,由 Twitter 提出,它能够在分布式系统中生成64位的全局唯一ID。其ID由时间戳、机器ID和序列号组成,能保证在高并发情况下生成不重复的数字。

代码语言:java
复制
public class SnowflakeIdGenerator {
    private final long twepoch = 1288834974657L;
    private final long workerIdBits = 5L;
    private final long datacenterIdBits = 5L;
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private final long sequenceBits = 12L;
    private final long workerIdShift = sequenceBits;
    private final long datacenterIdShift = sequenceBits + workerIdBits;
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long workerId;
    private long datacenterId;
    private long sequence = 0L;
    private long lastTimestamp = -1L;

    public SnowflakeIdGenerator(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;
    }

    public synchronized long nextId() {
        long timestamp = timeGen();

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

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        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();
    }
}
代码解析:
  • 时间戳:用于确保生成的ID按照时间顺序递增。
  • 机器ID和数据中心ID:用于在分布式系统中标识不同的机器和数据中心,防止ID冲突。
  • 序列号:在同一毫秒内生成多个ID时,用于区分这些ID。

雪花算法生成的ID是一个64位长的整数,能够在分布式环境下保证唯一性,且生成速度非常快。

使用案例分享

案例 1:基于自增数字生成订单号

对于中小型电商平台,生成唯一订单号的方式可以通过自增数字结合业务标识来完成。如下所示:

代码语言:java
复制
public class OrderService {
    private static long orderId = 0;

    public synchronized static String generateOrderNumber() {
        return "ORDER" + (++orderId);
    }
}

案例 2:分布式系统中的唯一标识生成

对于分布式系统,雪花算法是一种常见的解决方案。下面是一个分布式用户ID生成的示例:

代码语言:java
复制
public class UserIdGenerator {
    private static final SnowflakeIdGenerator idGenerator = new SnowflakeIdGenerator(1, 1); // 假设机器ID和数据中心ID为1

    public static long generateUserId() {
        return idGenerator.nextId();
    }
}

应用场景案例

  1. 订单号生成:在电商系统中,需要为每个订单生成唯一的订单号,避免重复的订单处理和数据混乱。
  2. 分布式系统中的唯一标识生成:在分布式架构中,多个节点同时进行任务时,生成全局唯一的ID是保障数据

一致性的关键。

优缺点分析

自增数字

  • 优点:实现简单,易于管理。
  • 缺点:仅适用于单机环境,多线程环境下需要同步处理,且不适合分布式系统。

时间戳结合随机数

  • 优点:能够在大多数场景下保证唯一性,生成速度较快。
  • 缺点:在高并发环境下有可能出现重复,随机数的范围较小。

UUID

  • 优点:能够生成几乎全局唯一的标识,且使用简单。
  • 缺点:UUID较长,不适合需要短ID的场景。

雪花算法

  • 优点:适合分布式环境,能够保证生成ID的唯一性和有序性。
  • 缺点:实现较为复杂,需要合理配置机器ID和数据中心ID。

核心类方法介绍

System.currentTimeMillis()

返回当前时间的毫秒数,自1970年1月1日开始计算。

Random.nextInt(int bound)

生成一个在 [0, bound) 范围内的随机整数。

UUID.randomUUID()

生成一个128位的随机UUID。

SnowflakeIdGenerator.nextId()

生成一个唯一的64位ID,用于分布式环境下的唯一标识生成。

测试用例

用例1:测试自增数字生成

代码语言:java
复制
@Test
public void testIncrementalNumberGeneration() {
    long num1 = IncrementalNumberGenerator.getNextNumber();
    long num2 = IncrementalNumberGenerator.getNextNumber();
    assertNotEquals(num1, num2);
}

代码解析:

如下是具体的代码解析,希望对大家有所帮助:

这段Java代码定义了一个测试方法 testIncrementalNumberGeneration,用于测试增量数字生成器是否能够生成不同的连续数字。

下面是这段代码的详细解读:

  1. @Test:这是一个JUnit注解,表示接下来的方法是测试方法。
  2. public void testIncrementalNumberGeneration() { ... }:定义了一个名为 testIncrementalNumberGeneration 的测试方法。
  3. long num1 = IncrementalNumberGenerator.getNextNumber();:调用 IncrementalNumberGenerator 类的静态方法 getNextNumber 来生成第一个数字,并将其存储在变量 num1 中。
  4. long num2 = IncrementalNumberGenerator.getNextNumber();:再次调用 getNextNumber 方法生成第二个数字,并将其存储在变量 num2 中。
  5. assertNotEquals(num1, num2);:使用 assertNotEquals 断言方法来验证 num1num2 是否不同。如果两个数字不相同,测试将通过;如果相同,则测试将失败。

总结:这个测试用例的目的是验证增量数字生成器生成的两个连续数字是否不相同。增量数字生成器通常用于确保每个生成的数字都是唯一的,并且每个后续数字都比前一个大,这在生成序列号、版本号等时非常有用。

注意:代码中假设 IncrementalNumberGenerator 类已经定义,并且它的 getNextNumber 方法能够生成连续的数字。此外,测试方法的名称表明它专注于数字生成器的功能,确保每次调用 getNextNumber 方法都能得到一个更大的数字。如果 IncrementalNumberGenerator 是多线程安全的,那么即使在并发环境下,这个测试也应该能够通过。

用例2:测试雪花算法生成唯一ID

代码语言:java
复制
@Test
public void testSnowflakeIdGeneration() {
    SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);
    long id1 = generator.nextId();
    long id2 = generator.nextId();
    assertNotEquals(id1, id2);
}

代码解析:

如下是具体的代码解析,希望对大家有所帮助:

这段Java代码定义了一个测试方法 testSnowflakeIdGeneration,用于测试雪花算法(Snowflake Algorithm)ID生成器是否能够生成不同的ID。

下面是这段代码的详细解读:

  1. @Test:这是一个JUnit注解,表示接下来的方法是测试方法。
  2. public void testSnowflakeIdGeneration() { ... }:定义了一个名为 testSnowflakeIdGeneration 的测试方法。
  3. SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1, 1);:创建了 SnowflakeIdGenerator 类的一个实例,这个类可能是一个实现了Twitter雪花算法的ID生成器。它的构造函数接受两个参数,通常表示数据中心ID和机器ID。
  4. long id1 = generator.nextId();:调用 generator 实例的 nextId 方法生成第一个ID,并将其存储在变量 id1 中。
  5. long id2 = generator.nextId();:再次调用 nextId 方法生成第二个ID,并将其存储在变量 id2 中。
  6. assertNotEquals(id1, id2);:使用 assertNotEquals 断言方法来验证 id1id2 是否不同。如果两个ID不相同,测试将通过;如果相同,则测试将失败。

总结:这个测试用例的目的是验证ID生成器生成的两个连续ID是否不相同。雪花算法ID生成器通常用于分布式系统中生成唯一的ID,它结合了时间戳、数据中心ID和机器ID来确保生成的ID的唯一性。

小结

本文通过多种方案介绍了如何在 Java 中生成永不重复的数字。从简单的自增数字到适用于分布式环境的雪花算法,各种方案适用于不同的场景。对于单机环境,简单的自增数字或时间戳结合随机数足够使用,而在分布式环境下,雪花算法则成为了最佳选择。

总结

Java 生成不重复数字的方案多种多样,开发者需要根据具体的应用场景选择最合适的方案。本文从单机环境到分布式系统,依次分析了自增、时间戳结合随机数、UUID和雪花算法,并提供了相关代码和案例。掌握这些方案,可以帮助开发者在实际项目中应对不同的唯一标识生成需求,保证系统的稳定性和数据的一致性。

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

... ...

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!

***

⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 摘要
  • 概述
    • 常见的生成方式
  • 源码解析
    • 1. 自增数字生成器
      • 代码解析:
    • 2. 时间戳结合随机数生成
      • 代码解析:
    • 3. UUID 生成
      • 代码解析:
    • 4. 雪花算法(Snowflake)
      • 代码解析:
  • 使用案例分享
    • 案例 1:基于自增数字生成订单号
    • 案例 2:分布式系统中的唯一标识生成
  • 应用场景案例
  • 优缺点分析
    • 自增数字
    • 时间戳结合随机数
    • UUID
    • 雪花算法
  • 核心类方法介绍
    • System.currentTimeMillis()
    • Random.nextInt(int bound)
    • UUID.randomUUID()
    • SnowflakeIdGenerator.nextId()
  • 测试用例
    • 用例1:测试自增数字生成
    • 用例2:测试雪花算法生成唯一ID
  • 小结
  • 总结
  • 文末
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档