首页 > 其他分享 >一个简单的ID生成器,请帮忙看看还有那些毛病

一个简单的ID生成器,请帮忙看看还有那些毛病

时间:2022-11-03 11:58:04浏览次数:52  
标签:String Thread 生成器 static split 毛病 new ID

业务系统经常需要生成各种唯一ID,想到UUID、雪花算法等;

UUID字符没有含义,掏出来给客户看,比较的很丑;

雪花算法是64位的,小业务用户感觉太长了,有点不满意;

琢磨了好些天,自己写了一个,完成初步测试没有重复;把代码贴出来请各位程序大佬指教;欢迎留意给出各种意见;

代码如下:

/**
 * @Author PeterShen
 * @Date 2022/9/17 9:32
 * @Description 构建唯一ID:当前时间的秒数(12)+随机(2)+IP信息(6)+自增(1-7)
 * @Version 1.0
 */
public class IdBuildHelper {
    /**
     * 计数的最大值 1千万(最大七位)
     * 防止计数爆掉
     */
    static final int MAX_COUNT = 9989999;
    static final Object lockObject = new Object();
    /**
     * 随机数生成
     */
    static Random random = new Random();
    static SimpleDateFormat formatter = new SimpleDateFormat("yyMMddHHmmss");
    /**
     * 每秒自增计数计数器
     */
    static volatile AtomicInteger counter = new AtomicInteger(0);
    /**
     * 当前的秒数,必须存储为秒级,不能是毫秒级,防止频繁换秒操作
     */
    static volatile long currentSecond = CurrentTimeMillisClock.getInstance().now() /1000;
    //服务的端口
    static int serverPort = 80;
    //服务的IP地址
    static String serviceIp = null;

    /**
     * 根据日期构建一个唯一ID
     * 由日期(12位)+两位随机+六位IP信息+自增(1-7位) 总位数:21-27位
     * @return
     */
    public static String buildId() {
        long second;
        int i;
        synchronized (lockObject){
            //加锁获取一份生成ID因子
            second = currentSecond;
            i = counter.incrementAndGet();
        }

        //根据当前日期判断是否需要换秒,
        Long integer = CurrentTimeMillisClock.getInstance().now() /1000;
        //为了避免重复,日期正常只能往前推进,如果改了过去的时间,一开始拒绝重置计数,直到计数达到最大值,不得已才重置
        if (currentSecond < integer || counter.get() > MAX_COUNT) {
            synchronized (lockObject){
                if (currentSecond < integer || counter.get() > MAX_COUNT) {
                    //重置计数
                    counter.set(0);
                    //换秒
                    if (currentSecond < integer) {
                        currentSecond = integer;
                    } else if (counter.get() > MAX_COUNT) {
                        //计数满,强制换秒
                        currentSecond++;
                    }
                }
            }
        }
        //根据因子生成
        return formatter.format(second*1000) + getTowBitRandom() + getIpStr() + i;
    }

    /**
     * 获取两位随机数
     *
     * @return
     */
    private static String getTowBitRandom() {
        return String.format("%02d", random.nextInt(99));
    }


    /**
     * 获取日期秒级(12位)字符串
     *
     * @return
     */
    private static String getDateSecondStr(long second) {
        return formatter.format(second*1000);
    }

    /**
     * 获取IP后面两段(6位)
     * 注意:这里有个假设,多个服务实例会不是在同一个网段
     *
     * @return
     */
    private static String getIpStr() {
        if (serviceIp == null) {
            try {
                InetAddress address = InetAddress.getLocalHost();
                String ip = address.getHostAddress();
                //获取IP后面两段
                String[] split = ip.split("\\.");
                if (split.length > 0) {
                    int n = Integer.valueOf(split[split.length - 2] + split[split.length - 1]);
                    //与服务端口做异或运算,隐藏真实IP
                    n = (n ^ serverPort) << 1;
                    serviceIp = String.format("%06d", n);
                } else {
                    serviceIp = "";
                }

            } catch (UnknownHostException e) {
                serviceIp = "";
            }
        }
        return serviceIp;
    }

    /**
     * 设置服务器执行端口:默认80
     * 为了防止同一个IP 用不同端口启用多个实例,建议填入
     */
    public static void setServerPort(int port) {
        IdBuildHelper.serverPort = port;
    }

}

对应的测试代码:

    @Test
    void multiThreadTest() throws InterruptedException {
        Set<String> codeSet = new ConcurrentHashSet<>(5000000);
        Thread[] threads = new Thread[10];
        for (int i = 0; i < 10; i++) {
            Thread t = new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println( Thread.currentThread().getName()+"开始执行");
                    for (int j = 0; j < 500000; j++) {
                        String s = IdBuildHelper.buildId();
                        boolean add = codeSet.add(s);
                        if(!add){
                            System.out.println("发生重复"+ s+":"+j);
                        }
                    }
                    System.out.println( Thread.currentThread().getName()+"执行完成");
                    System.out.println("容器内容数:"+ codeSet.size());
                }
            });
            t.setName("线程"+i);
            threads[i] = t;
        }
        for (Thread thread : threads) {
            thread.start();
        }
        for (Thread thread : threads) {
            thread.join();
        }
        if(codeSet.size()<5000000){
            System.out.println("生成异常");
        }
        codeSet.clear();
    }

感受:

几行简单的代码真的琢磨了好久,反复改了好多次;主要反复的点是在:1.多线程重复问题;2.生成性能问题;3.生成的ID合适规范性的问题;然后感觉自己能力有限,总是考虑不周到,需要继续学习;

如您在阅读时遇到任何疑问也欢迎留言,感觉这是个有趣的东西,所以贴出来,总之欢迎多交流。

 

标签:String,Thread,生成器,static,split,毛病,new,ID
From: https://www.cnblogs.com/splyn/p/16853964.html

相关文章

  • OpenCV VideoCapture 读取视频并且获取一帧和帧率
    前言这篇文章主要讲述如何读取视频并且获取一帧图像和帧率一、代码1#include<opencv2/opencv.hpp>2#include<iostream>3usingnamespacecv;4intmain()......
  • 配置Squid代理服务器
    添加双网卡vm1是内网同学vm2是外网通信Squid服务器生成外网网卡配置文件外网网卡设置IP地址然后重启服务查看网卡生效了没修改配置文件开启路由功能更新内核参数开启路由转......
  • Set-UID
    ......
  • 为什么HTTP代理会出现“返回403 forbidden”
    平时我们在使用HTTP代理的过程中,稍有不慎就会出现各种各样的错误代码,其中“403forbidden”就是常见的一种。它属于HTTP协议中的一个状态码(StatusCode),可以简单的理......
  • 学习vue3(五)(插槽slot,Teleport传送组件,keep-alive缓存组件,依赖注入Provide / Inject)
    插槽slot插槽就是子组件中的提供给父组件使用的一个占位符,用<slot></slot>表示,父组件可以在这个占位符中填充任何模板代码,如HTML、组件等,填充的内容会替换子组件的<slot......
  • 2022-11-03 v-html will override element children.
    v-htmlwilloverrideelementchildren. v-html将覆盖元素子级。源代码:<divv-html="title"class="title">{{title}}</div>原因:使用了v-html的标签,该标签里面不能......
  • fiddler的基础操作
    接口的组成url+请求方式+响应=接口(url+域名+端口号+方法名+入参)请求方法:post/get请求参数:username、password、tel、idcard入参类型:json、xml、form返回:状态码(code)+......
  • 20. Valid Parentheses
    Givenastringcontainingjustthecharacters '(', ')', '{', '}', '[' and ']',determineiftheinputstringisvalid.Thebracketsmustcloseintheco......
  • nginx “403 Forbidden” 错误的原因及解决办法
    多数是权限问题root/home/jd/code/dapingdist;这里要从home---->jd----->code,从外向内,一层层排查,是否有可读权限1.权限配置不正确这个是nginx出现403forbidden......
  • IDEA研究院
    粤港澳大湾区数字经济研究院(InternationalDigitalEconomyAcademy,简称“IDEA研究院”),https://idea.edu.cn/论文阅读平台ReadPaper,https://idea.edu.cn/readpaper.html......