使用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();
            }
        }
    }
}

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

相关文章
|
17天前
主键自增
主键自增。
14 4
|
存储 算法 安全
订单号和 id 列可不可以是同一列?
在分布式场景中,单表已经不能满足我们的需求了,所以用自增 id 的方案也就不合适了。当比如我们进行分表设计时,主键列到底如何生成就成了一个问题,流行的方法是利用像 snowflake 这样的算法计算出一个趋势有序的值作为 id。(当然还有其他多种方法)这样就满足了扩展性和一定程度上解决了检索性能的问题。
订单号和 id 列可不可以是同一列?
|
9月前
|
存储 关系型数据库 MySQL
InnoDB为什么使用自增id作为主键
InnoDB是MySQL数据库中一种常用的存储引擎,它使用自增id作为主键的设计是出于多方面的考虑。
260 0
|
10月前
|
SQL 关系型数据库 MySQL
列的完整性约束——设置表字段的非空约束(NOT NULL, NK)
列的完整性约束——设置表字段的非空约束(NOT NULL, NK)
|
存储 缓存 Oracle
关于自增id 你可能还不知道
在使用MySQL建表时,我们通常会创建一个自增字段(AUTO_INCREMENT),并以此字段作为主键。本篇文章将以问答的形式讲述关于自增id的一切。 注: 本文所讲的都是基于Innodb存储引擎。
215 0
|
存储 SQL 关系型数据库
MySQL 建表为啥设置自增 id?用流水号当主键不正好么?
本文主要通过查阅资料,了解为什么要设置一个和业务无关的自增 id 用来当做主键,很多内容比较浅显,比如 InnoDB 的 B+ 树,页分裂及页合并,插入过程等都没有进行深入研究,有兴趣的小伙伴可以更深入的研究下。 同时在建表时除了要设置一个自增 id 用来当做主键,小伙伴们在业务开发过程中是否也会遇到一种情况:用户的注销,数据的删除等都是进行的逻辑删除,而不是物理删除。 本篇文章介绍比较简陋,不足之处,希望大家多多指正。
549 0
|
存储 Java
关于自增操作,你真的懂了吗?
最近看见一道有意思的面试题,是关于自增操作的,让我回想起以前自己也遇到过,并且曾经也让我困惑过,今天拿出来跟大家分享,希望对大家有帮助。
159 0
关于自增操作,你真的懂了吗?
【1041】Be Unique (20 分)
【1041】Be Unique (20 分) 【1041】Be Unique (20 分)
73 0