全局唯一编码ID生成器

本文介绍了一种生成全局唯一ID的方法,通过结合时间戳(17或13位)、机器码和进程号,并利用循环序列号来确保唯一性。展示了两种不同长度版本的实现:31位格式化和27位非格式化版本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

全局唯一编码ID生成器

package com.util;

import java.lang.management.ManagementFactory;
import java.net.NetworkInterface;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
import java.util.Enumeration;

import org.apache.commons.lang3.time.DateFormatUtils;

/**
 * <p>
 * 全局唯一ID生成器,31位:17位毫秒时间戳(格式化后)+9位机器码和进程号+5位循环序列号
 * </p>
 * <p>
 * 全局唯一ID生成器,27位:13位毫秒数时间戳(无格式化)+9位机器码和进程号+5位循环序列号
 * </p>
 */
public class GeneratorId {

    public static void main(String[] args) {
        long a = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
        System.out.println(nextFormatId());
            nextFormatId();
        }
        System.out.println(System.currentTimeMillis() - a);

        System.out.println(nextMillisId());
    }

    /**
     * 流水号起始值
     */
    private static final long MIN_SEQUENCE = 10000L;
    /**
     * 流水号最大值
     */
    private static final long MAX_SEQUENCE = 99999L;
    /**
     * 流水号,在最小最大值之间循环,避免唯一ID的安全性泄露,时间戳精确到毫秒,单机在1毫秒内也不可能生成近十万个业务ID
     */
    private static long sequence = MIN_SEQUENCE;
    /**
     * 机器码 加 进程号 会导致生成的序列号很长, 基于这两个值做一些截取
     */
    private static final String MP;

    static {
        try {
            // 机器码 --> 本机 mac 地址的 hashcode 值
            int machineIdentifier = createMachineIdentifier();
            // 进程号 --> 当前运行的 jvm 进程号的 hashcode 值
            int processIdentifier = createProcessIdentifier();

            int hashcode = (machineIdentifier + "" + processIdentifier).hashCode();
            String mp = "";
            if (hashcode != Integer.MIN_VALUE) {
                mp = Integer.toString(Math.abs(hashcode));
            } else {
                mp = Integer.toString(Integer.MIN_VALUE);
            }
            MP = mp;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private GeneratorId() {
    }

    /**
     * 全局唯一ID生成器,31位:17位毫秒时间戳(格式化后)+9位机器码和进程号+5位循环序列号
     */
    public static synchronized String nextFormatId() {
        return DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMddHHmmssSSS") + MP
            + nextSequence();
    }

    /**
     * 全局唯一ID生成器,27位:13位毫秒数时间戳(无格式化)+9位机器码和进程号+5位循环序列号
     */
    private static synchronized String nextMillisId() {
        return System.currentTimeMillis() + MP + nextSequence();
    }

    /**
     * 生成下一个序列号
     */
    private static synchronized long nextSequence() {
        if (sequence >= MAX_SEQUENCE) {
            sequence = MIN_SEQUENCE;
        }
        ++sequence;
        return sequence;
    }

    /**
     * 获取机器标识符
     */
    private static int createMachineIdentifier() {
        // build a 2-byte machine piece based on NICs info
        int machinePiece;
        try {
            StringBuilder stringBuilder = new StringBuilder();
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface
                .getNetworkInterfaces();
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface networkInterface = networkInterfaces.nextElement();
                stringBuilder.append(networkInterface.toString());
                byte[] mac = networkInterface.getHardwareAddress();
                if (mac != null) {
                    ByteBuffer byteBuffer = ByteBuffer.wrap(mac);
                    try {
                        stringBuilder.append(byteBuffer.getChar());
                        stringBuilder.append(byteBuffer.getChar());
                        stringBuilder.append(byteBuffer.getChar());
                    } catch (BufferUnderflowException shortHardwareAddressException) { // NOPMD
                        // mac with less than 6 bytes. continue
                    }
                }
            }
            machinePiece = stringBuilder.toString().hashCode();
        } catch (Throwable t) {
            // exception sometimes happens with IBM JVM, use random
            machinePiece = new SecureRandom().nextInt();
        }
        return machinePiece;
    }


    /**
     * 获取JVM进程号,这并不是每个类装入器,因为必须是唯一的
     */
    private static int createProcessIdentifier() {
        int processId;
        try {
            String processName = ManagementFactory.getRuntimeMXBean().getName();
            if (processName.contains("@")) {
                processId = Integer.parseInt(processName.substring(0, processName.indexOf('@')));
            } else {
                processId = processName.hashCode();
            }
        } catch (Throwable t) {
            processId = new SecureRandom().nextInt();
        }
        return processId;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值