文档章节

java单机生成不重复增长15位流水号方式

王博-北京
 王博-北京
发布于 2014/11/23 19:00
字数 1323
阅读 384
收藏 1
点赞 0
评论 2

开发中,经常需要获取一些流水号,或称为ID,这些ID经常无业务意义,仅仅是为了区分不同的实体或数据库中的记录。UUID提供了一种非常好的策略,考虑到了时间、机器ID(涉及CPU编号、网卡MAC等等物理信息)、随机值等信息,能生成在时空中“唯一”的字符串,需要注意,此唯一并不是绝对唯一,如果不考虑主动造假和作弊,目前能保证唯一。

在具体的开发中,UUID生成的UUID字符串过长,不太适合业务场景。而且如果是单机场景,还有点浪费。

我在开发过程中,使用了这样一种方法,来解决唯一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);
	}
}


© 著作权归作者所有

共有 人打赏支持
王博-北京
粉丝 3
博文 8
码字总数 3730
作品 0
海淀
项目经理
加载中

评论(2)

王博-北京
王博-北京

引用来自“Anger_Coder”的评论

你可以不加锁呀,你让所有方法顺序执行就好了,不用多线程呀!
可是在真实的环境中,是多线程情况。得保证在多线程情况下 获得唯一的ID。问题已经找见了,问题出在测试使用的集合HashSet,该类不是线程安全的,其add方法不保证线程安全。所以测试结果错误。
Anger_Coder
Anger_Coder
你可以不加锁呀,你让所有方法顺序执行就好了,不用多线程呀!
MySQL多数据源笔记5-ShardingJDBC实战

Sharding-JDBC集分库分表、读写分离、分布式主键、柔性事务和数据治理与一身,提供一站式的解决分布式关系型数据库的解决方案。 从2.x版本开始,Sharding-JDBC正式将包名、Maven坐标、码云仓...

狂小白 ⋅ 03/19 ⋅ 0

Java 虚拟机16:Metaspace

原文出处:五月的仓颉 被废弃的持久代 想起之前面试的时候有面试官问起过我一个问题:Java 8为什么要废弃持久代即Metaspace的作用。由于当时使用的Java 7且研究重心不在JVM上,一下没有回答上...

五月的仓颉 ⋅ 04/13 ⋅ 0

【死磕Sharding-jdbc】—–分布式ID

原文作者:阿飞Javaer 原文链接:https://www.jianshu.com/p/7f0661ddd6dd 实现动机 传统数据库软件开发中,主键自动生成技术是基本需求。而各大数据库对于该需求也提供了相应的支持,比如M...

飞哥-Javaer ⋅ 05/05 ⋅ 0

Java面试2018常考题目汇总及答案带走不谢!

一、JAVA基础篇-概念 1.简述你所知道的Linux: Linux起源于1991年,1995年流行起来的免费操作系统,目前, Linux是主流的服务器操作系统, 广泛应用于互联网、云计算、智能手机(Android)等...

java高级架构牛人 ⋅ 06/14 ⋅ 0

Python 人气王,JS 比 Java 更受公司青睐

四月 PYPL 编程语言排行榜和 Hacker News 招聘趋势都已经出炉。 Hacker News 招聘趋势的数据由 Hacker News 新闻站负责收集,它不仅仅关注编程语言趋势,还关注公司要求的顶尖技能的趋势,包...

达尔文 ⋅ 04/23 ⋅ 17

zookeeper单机多实例部署

主题介绍 介绍zookeeper单机多实例部署,更多适合于实验性质;ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,...

computer306 ⋅ 04/22 ⋅ 0

Java虚拟机标准(第10版)第一章(节选)翻译与评注

英文原文链接:https://docs.oracle.com/javase/specs/jvms/se10/html/jvms-1.html 评注是括在鱼尾号之间的文字,其余均为翻译 Java虚拟机是Java平台的基石,这种技术实现了诸如跨平台、生成...

Jelif ⋅ 06/03 ⋅ 0

深入理解 ThreadLocal (这些细节不应忽略)

前言 对于 ThreadLocal 的使用,并不难。但要深入理解 ThreadLocal 的实现方式,需要细细揣摩。写本文前,我在网上看了很多关于 ThreadLocal 的分析,但却感到遗憾,因为很多文章存在着一定误...

徐志毅 ⋅ 04/11 ⋅ 0

Java编程学习:集合框架详解

Java是一种可以撰写跨平台应用软件的面向对象的程序设计语言。Java 技术具有卓越的通用性、高效性、平台移植性和安全性,广泛应用于PC、数据中心、游戏控制台、科学超级计算机、移动电话和互...

Java小辰 ⋅ 05/30 ⋅ 0

centos7 yum安装java运行环境,初识hadoop

安装java运行环境 1.实验机相关信息: [root@node2 ~]# cat /etc/redhat-release CentOS Linux release 7.2.1511 (Core) [root@node2 ~]# uname -r 3.10.0-327.el7.x86_6 2.配置epel源,以y......

smile68 ⋅ 04/21 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

uWSGI + Django @ Ubuntu

创建 Django App Project 创建后, 可以看到路径下有一个wsgi.py的问题 uWSGI运行 直接命令行运行 利用如下命令, 可直接访问 uwsgi --http :8080 --wsgi-file dj/wsgi.py 配置文件 & 运行 [u...

袁祾 ⋅ 32分钟前 ⋅ 0

JVM堆的理解

在JVM中,我们经常提到的就是堆了,堆确实很重要,其实,除了堆之外,还有几个重要的模块,看下图: 大 多数情况下,我们并不需要关心JVM的底层,但是如果了解它的话,对于我们系统调优是非常...

不羁之后 ⋅ 昨天 ⋅ 0

推荐:并发情况下:Java HashMap 形成死循环的原因

在淘宝内网里看到同事发了贴说了一个CPU被100%的线上故障,并且这个事发生了很多次,原因是在Java语言在并发情况下使用HashMap造成Race Condition,从而导致死循环。这个事情我4、5年前也经历...

码代码的小司机 ⋅ 昨天 ⋅ 1

聊聊spring cloud gateway的RetryGatewayFilter

序 本文主要研究一下spring cloud gateway的RetryGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config/G......

go4it ⋅ 昨天 ⋅ 0

创建新用户和授予MySQL中的权限教程

导读 MySQL是一个开源数据库管理软件,可帮助用户存储,组织和以后检索数据。 它有多种选项来授予特定用户在表和数据库中的细微的权限 - 本教程将简要介绍一些选项。 如何创建新用户 在MySQL...

问题终结者 ⋅ 昨天 ⋅ 0

android -------- 颜色的半透明效果配置

最近有朋友问我 Android 背景颜色的半透明效果配置,我网上看资料,总结了一下, 开发中也是常常遇到的,所以来写篇博客 常用的颜色值格式有: RGB ARGB RRGGBB AARRGGBB 这4种 透明度 透明度...

切切歆语 ⋅ 昨天 ⋅ 0

CentOS开机启动subversion

建立自启动脚本: vim /etc/init.d/subversion 输入如下内容: #!/bin/bash## subversion startup script for the server## chkconfig: 2345 90 10# description: start the subve......

随风而飘 ⋅ 昨天 ⋅ 0

版本控制工具

CSV , SVN , GIT ,VSS

颖伙虫 ⋅ 昨天 ⋅ 0

【2018.06.19学习笔记】【linux高级知识 13.1-13.3】

13.1 设置更改root密码 13.2 连接mysql 13.3 mysql常用命令

lgsxp ⋅ 昨天 ⋅ 0

LVM

LVM: 硬盘划分分区成物理卷->物理卷组成卷组->卷组划分逻辑分区。 1.磁盘分区: fdisk /dev/sdb 划分几个主分区 输入t更改每个分区类型为8e(LVM) 使用partprobe生成分区的文件:如/dev/sd...

ZHENG-JY ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部