使用AtomicInteger实现自增长编号

简介: 使用AtomicInteger实现自增长编号

有个需求,根据日期来生成一个自增长编号,格式:2019090400001。

思路是使用AtomicInteger原子操作类,好处就是不会出现重复,在多线程操作环境下优势尤为明显,可以自行研究一下。

下面是实现代码

public class TextUtil {
    public static ConcurrentHashMap<String, AtomicInteger> ID = new ConcurrentHashMap<>();
    private static String timeFlag;
    static {
        //初始值        
        ID.put("num", new AtomicInteger(1));
    }
    /**
     * 生成案件编号,格式:2019082300001
     * @return
     */
    public static String getCaseNo() {
        String now = DateUtil.getDate();
        if (!now.equals(timeFlag)) {
            timeFlag = now;
            ID.get("num").getAndSet(1);
        }
        int nextNum = ID.get("num").getAndIncrement();
        String str = String.format("%05d", nextNum);
        now += str;
        return now;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            System.out.println(getCaseNo());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

测试结果

初步实现了功能,但是当项目重启后编号会重新开始计算。

这里考虑使用缓存机制把值存起来,项目重启时再次获取并赋值。

下面一个版本修改上述风险,使用文件存储,缺点就是增加了磁盘IO。

当然你可以使用更为高效的Redis或者其它技术。

public class TextUtil {
    public static ConcurrentHashMap<String, AtomicInteger> ID = new ConcurrentHashMap<>();
    private final static String file = TextUtil.class.getResource("").getPath() + "eventId.ini";
    private static String timeFlag;
    static {
        //初始值
        //程序启动时获取文件内容
        String str = read();
        int num = 1;
        if (StringUtils.isNotBlank(str)) {
            timeFlag = str.substring(0, 8);//最后更新日期,格式:yyyyMMdd
            String numStr = str.substring(8);
            num = Integer.parseInt(numStr);
            num++;
        }
        ID.put("num", new AtomicInteger(num));
    }
    /**
     * 生成编号,格式:2019082300001 
     *
     * @return
     */
    public static String getCaseNo() {
        String now = DateUtil.getDate();
        if (!now.equals(timeFlag)) {
            timeFlag = now;
            ID.get("num").getAndSet(1);
        }
        int nextNum = ID.get("num").getAndIncrement();
        store(nextNum + "");
        String str = String.format("%05d", nextNum);
        now += str;
        store(now);
        return now;
    }
    //存数据
    private static void store(String eventId) {
        //写入相应的文件
        BufferedOutputStream out = null;
        try {
            out = new BufferedOutputStream(new FileOutputStream(file));
            byte[] bytes = eventId.getBytes();
            //写入文件
            out.write(bytes, 0, bytes.length);
            //清缓存
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    //清缓存
                    out.flush();
                    out.close();
                } catch (Exception e) {
                }
            }
        }
    }
    //读数据
    private static String read() {
        String str = "";
        //读取文件(缓存字节流)
        BufferedInputStream in = null;
        try {
            File f = new File(file);
            if (!f.exists()) {
                f.createNewFile();
            }
            in = new BufferedInputStream(new FileInputStream(f));
            //读取数据
            //一次性取多少字节
            byte[] bytes = new byte[32];
            //接受读取的内容(n就代表的相关数据,只不过是数字的形式)
            int n = -1;
            //循环取出数据
            while ((n = in.read(bytes, 0, bytes.length)) != -1) {
                //转换成字符串
                str = new String(bytes, 0, n, "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //关闭流
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return str;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            System.out.println(getCaseNo());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

再次测试的时候就有连续性了

相关文章
|
索引 Python
全解析!9个处理Excel的Python库,到底哪个最好用?
全解析!9个处理Excel的Python库,到底哪个最好用?
7169 1
全解析!9个处理Excel的Python库,到底哪个最好用?
|
前端开发
react-antd中使用Upload实现图片裁剪-上传-预览的功能
使用react中antd实现图片的上传裁剪和预览,记录一下实现过程,希望能对大家有帮助
1516 0
react-antd中使用Upload实现图片裁剪-上传-预览的功能
|
9月前
|
XML Java 测试技术
使用 @Transactional 控制事务边界:传播和隔离解释
本文深入解析了 Spring 框架中的 `@Transactional` 注解,重点介绍了事务管理中的传播行为与隔离级别。内容涵盖事务的基本概念、声明式事务管理、回滚机制、传播模式(如 REQUIRED、REQUIRES_NEW 等)及隔离级别(如 READ_COMMITTED、SERIALIZABLE),并通过示例说明如何在实际开发中灵活应用这些特性,以确保数据一致性与系统性能的平衡。适合 Java 开发人员深入理解 Spring 事务机制。
753 1
使用 @Transactional 控制事务边界:传播和隔离解释
|
监控 Java API
1K star!这个开源项目让短信集成简单到离谱,开发效率直接翻倍!
SMS4J 是一款由国内技术团队打造的短信聚合框架,专为解决多短信服务商接入难题而生。它就像短信界的"瑞士军刀",目前已整合21家主流短信服务商,从阿里云、腾讯云到中国移动云MAS,开发者只需通过简单配置即可实现多平台无缝切换。
1011 4
|
SQL Ubuntu 关系型数据库
PostgreSQL介绍和PostgreSQL包安装
PostgreSQL 是一个功能强大、可扩展的开源关系型数据库系统,以其可靠性、数据完整性和高性能著称。它支持复杂查询、事务、多版本并发控制及丰富的数据类型,适用于各种应用场景。本文介绍 PostgreSQL 的核心特性,并详细说明在多种 Linux 发行版上的安装与配置方法,帮助用户快速部署和使用该数据库系统。
1192 0
|
前端开发
如何在React Router中进行路由重定向?
如何在React Router中进行路由重定向?
805 57
|
移动开发 前端开发 JavaScript
React 图片裁剪组件 Image Cropper
本文介绍了在React中实现图片裁剪功能的方法,涵盖基础知识、常见问题及解决方案。首先,通过第三方库如`react-image-crop`或`cropperjs-react`可轻松实现图片裁剪。接着,针对性能和兼容性问题,提供了优化图片加载、处理裁剪区域响应慢、解决浏览器差异等方案。最后,通过代码案例详细解释了如何创建一个基本的图片裁剪组件,并提出了优化建议,如使用`React.memo`、添加样式支持及处理大图片预览,帮助开发者避免常见错误并提升用户体验。
999 67
|
Java 应用服务中间件 编译器
详解JAVA远程debug
详解JAVA远程debug
997 0
|
存储 安全 API
12种最基本Web API:开发者的必学清单⭐
这些Web API 为创建高度互动和用户友好的网页应用开辟了无限可能。从存储和支付到地理位置和图形,掌握这些 API 可以提升您的Web开发技能。
888 2
|
机器学习/深度学习 人工智能 自然语言处理