java单机生成不重复增长15位流水号方式
2016-09-05 17:03
447 查看
原文地址
开发中,经常需要获取一些流水号,或称为ID,这些ID经常无业务意义,仅仅是为了区分不同的实体或数据库中的记录。UUID提供了一种非常好的策略,考虑到了时间、机器ID(涉及CPU编号、网卡MAC等等物理信息)、随机值等信息,能生成在时空中“唯一”的字符串,需要注意,此唯一并不是绝对唯一,如果不考虑主动造假和作弊,目前能保证唯一。
在具体的开发中,UUID生成的UUID字符串过长,不太适合业务场景。而且如果是单机场景,还有点浪费。
ps.setString(1, UUID.randomUUID().toString().replaceAll("-", ""));
我在开发过程中,使用了这样一种方法,来解决唯一ID的生成,又不至于太长。
1)获取系统当前时间的毫秒值,系统一般返回的是Long型,将该值转换成16进制,目的是为了缩小生成结果字符串的长度。
2)使用一个全局int变量,每次获取唯一ID时,该变量自增 1,并追加到第一步中的字符串后面。如果想保持获取到的字符串长度一致,可以补0字符串。
在上面所述的方案中,为了在单位时间内生成的唯一ID尽量多,将自增的int值转换成36进制(10位数字+26位字母),这样就使单位毫秒内生成的唯一ID数量在36的N次方个。N=打算生成多少位字符串。如果打算生成15位唯一ID,则当前时间会占去11位(16进制的字符形式),4位36进制字符,大约在160万个。也就是说,1 毫秒内生成 160万个字符不重复。
此种方案主要的核心是:如何保证全局共享的int自增数值在多线程环境下,能原子性的自增呢?答案是 同步控制 或者 AtomicInteger类。
简单的实现和测试类如下:
思路如下:1)main方法中启动了20个线程,每个线程循环获取100个字符串,并将字符串放置入 全局的hashSet中,最后打印出hashSet的长度。如果恰好是2000,则证明没问题,否则存在问题;2)生成15位字符串的方法是 获取当前时间毫秒值,并转换成16进制,然后使用AtomicInteger的自增并获取的原子方法。最后补全位数15位。
开发中,经常需要获取一些流水号,或称为ID,这些ID经常无业务意义,仅仅是为了区分不同的实体或数据库中的记录。UUID提供了一种非常好的策略,考虑到了时间、机器ID(涉及CPU编号、网卡MAC等等物理信息)、随机值等信息,能生成在时空中“唯一”的字符串,需要注意,此唯一并不是绝对唯一,如果不考虑主动造假和作弊,目前能保证唯一。
在具体的开发中,UUID生成的UUID字符串过长,不太适合业务场景。而且如果是单机场景,还有点浪费。
ps.setString(1, UUID.randomUUID().toString().replaceAll("-", ""));
我在开发过程中,使用了这样一种方法,来解决唯一ID的生成,又不至于太长。
1)获取系统当前时间的毫秒值,系统一般返回的是Long型,将该值转换成16进制,目的是为了缩小生成结果字符串的长度。
2)使用一个全局int变量,每次获取唯一ID时,该变量自增 1,并追加到第一步中的字符串后面。如果想保持获取到的字符串长度一致,可以补0字符串。
在上面所述的方案中,为了在单位时间内生成的唯一ID尽量多,将自增的int值转换成36进制(10位数字+26位字母),这样就使单位毫秒内生成的唯一ID数量在36的N次方个。N=打算生成多少位字符串。如果打算生成15位唯一ID,则当前时间会占去11位(16进制的字符形式),4位36进制字符,大约在160万个。也就是说,1 毫秒内生成 160万个字符不重复。
此种方案主要的核心是:如何保证全局共享的int自增数值在多线程环境下,能原子性的自增呢?答案是 同步控制 或者 AtomicInteger类。
简单的实现和测试类如下:
思路如下:1)main方法中启动了20个线程,每个线程循环获取100个字符串,并将字符串放置入 全局的hashSet中,最后打印出hashSet的长度。如果恰好是2000,则证明没问题,否则存在问题;2)生成15位字符串的方法是 获取当前时间毫秒值,并转换成16进制,然后使用AtomicInteger的自增并获取的原子方法。最后补全位数15位。
import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; /** * * 流水号生成器 * * @project * @version v1.0 * @author wangboak (wangboak@126.com) * @createDate 2014年10月7日 下午2:01:00 * @company (C) Copyright 迅谨(北京)科技有限公司 2014-2114<br/> * @since JDK 1.7 * */ public class SNUtil { private final static String str62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private final static int pixLen = 36; private static volatile int pixOne = 0; private static volatile int pixTwo = 0; private static volatile int pixThree = 0; private static volatile int pixFour = 0; private static final AtomicInteger ATOM_INT = new AtomicInteger(0); private static final int MAX_36 = 36 * 36 * 36 * 36; /** * 生成短时间内不会重复的长度为15位的字符串。<br/> * 生成策略为获取自1970年1月1日零时零分零秒至当前时间的毫秒数的16进制字符串值,该字符串值为11位<br/> * 并追加四位"0-z"的自增字符串.<br/> * 如果系统时间设置为大于<b>2304-6-27 7:00:26<b/>的时间,将会报错!<br/> * 由于系统返回的毫秒数与操作系统关系很大,所以本方法并不准确。本方法可以保证在系统返回的一个毫秒数内生成36的4次方个(1679616)ID不重复。<br/> * 经过测试:该方法效率 比 create15_2 方法快一倍 * @return 15位短时间不会重复的字符串。<br/> * @author wangbo * @since JDK1.6 */ public final static synchronized String create15() { StringBuilder sb = new StringBuilder(15);// 创建一个StringBuilder sb.append(Long.toHexString(System.currentTimeMillis()));// 先添加当前时间的毫秒值的16进制 pixFour++; if (pixFour == pixLen) { pixFour = 0; pixThree++; if (pixThree == pixLen) { pixThree = 0; pixTwo++; if (pixTwo == pixLen) { pixTwo = 0; pixOne++; if (pixOne == pixLen) { pixOne = 0; } } } } return sb.append(str62.charAt(pixOne)).append(str62.charAt(pixTwo)).append(str62.charAt(pixThree)).append(str62.charAt(pixFour)) .toString(); } -------------------------------------------------------------------------------------------------------------------------------------------------- @SuppressWarnings("unused") private final static String create15_2() { StringBuilder sb = new StringBuilder(15); sb.append(Long.toHexString(System.currentTimeMillis())); ATOM_INT.compareAndSet(MAX_36, 0); String str = longTo36(ATOM_INT.incrementAndGet()); if (str.length() == 1) { sb.append("000").append(str); } else if (str.length() == 2) { sb.append("00").append(str); } else if (str.length() == 3) { sb.append("0").append(str); } else { sb.append(str); } return sb.toString(); } /** * 10进制转任意进制 * @param num Long型值 * @param base 转换的进制 * @return 任意进制的字符形式 */ private static final String ten2Any(long num, int base) { StringBuilder sb = new StringBuilder(7); while (num != 0) { sb.append(str62.charAt((int) (num % base))); num /= base; } return sb.reverse().toString(); } /** * 将一个Long 值 转换为 62进制 * @param num * @return */ public static final String longTo62(long num) { return ten2Any(num, 62); } private static final String longTo36(long num) { return ten2Any(num, 36); } }
相关文章推荐
- java单机生成不重复增长15位流水号方式
- 【java技术】日期+自增长 流水号生成策略
- 自动生成流水号, sql 方式, class 方式, ( 有待完成)
- java生成无重复随机数的2种方法
- java 一个int数组 长度为100 随机生成100个数 即1-100 将其插入进数组 插入的数字不能重复
- java使用iText生成pdf文档的对齐方式
- 前2天后台系统需要生成报表,正好抽时间复习了一下之前做过的JAVA生成EXCEL,下面介绍POI 和JXL 生成报表的2种方式。
- java中生成无重复随机数的2种方法
- 中软面试(一):java写一个随机生成四位数的程序 每位数字不重复
- java 不重复随机数生成
- java生成不重复的随机数
- java中生成不重复随机的数字
- java生成无重复随机数的2种方法
- Java 生成不重复的随机数
- java生成不重复的随机数
- [Java]一个简易的生成流水号的类
- 自动生成流水号-单一StoreProcedure方式
- java生成不重复的随机数
- 若干种生成java exe文件的方式
- JAVA自动生成sql结果到excel,并以附件方式发送到指定邮箱