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

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

相关文章
|
7月前
主键自增
主键自增。
41 4
|
存储 算法 安全
订单号和 id 列可不可以是同一列?
在分布式场景中,单表已经不能满足我们的需求了,所以用自增 id 的方案也就不合适了。当比如我们进行分表设计时,主键列到底如何生成就成了一个问题,流行的方法是利用像 snowflake 这样的算法计算出一个趋势有序的值作为 id。(当然还有其他多种方法)这样就满足了扩展性和一定程度上解决了检索性能的问题。
订单号和 id 列可不可以是同一列?
|
17天前
|
存储 关系型数据库 MySQL
InnoDB为什么使用自增id作为主键?
MySQL以数据页(默认16K)为单位存储数据。自增ID主键时,写满一页直接申请新页;非自增ID主键需保持索引有序,插入数据可能引发页分裂,即需将部分数据移至新页,影响插入效率。
29 6
|
7月前
|
缓存 算法 安全
被追着问UUID和自增ID做主键哪个好,为什么?
讨论了UUID和自增ID作为数据库主键的优缺点。UUID全局唯一,适合分布式系统,但存储空间大,不适合范围查询。自增ID存储空间节省,查询效率高,但分库分表困难,可预测性高。UUID版本包括基于时间戳(V1)、随机数(V4)以及基于名称空间的MD5(V3)和SHA1(V5)散列。
被追着问UUID和自增ID做主键哪个好,为什么?
|
7月前
redisTemplate进行自增操作例子
redisTemplate进行自增操作例子
redisTemplate进行自增操作例子
|
存储 关系型数据库 MySQL
InnoDB为什么使用自增id作为主键
InnoDB是MySQL数据库中一种常用的存储引擎,它使用自增id作为主键的设计是出于多方面的考虑。
460 0
|
SQL 关系型数据库 MySQL
Auto-increment 会在新记录插入表
Auto-increment 会在新记录插入表
105 0
记录插入顺序用linkedHashMap
记录插入顺序用linkedHashMap
113 0
|
存储 Java
关于自增操作,你真的懂了吗?
最近看见一道有意思的面试题,是关于自增操作的,让我回想起以前自己也遇到过,并且曾经也让我困惑过,今天拿出来跟大家分享,希望对大家有帮助。
216 0
关于自增操作,你真的懂了吗?