java id主键_JAVA主键ID生成工具类:改自twitter的分布式ID算法snowflake

祝大家新年快乐,有任何问题可与我联系:0965a7154146dd49b11cadccd18d91c7.png

关于snowflake算法的介绍和原理这里不过多说明了,网上有很多。

这里简单描述下SnowflakeUtil的优点:

1、做为底层工具使用,可用于数据库主键、订单编号……

2、不依赖数据库,速度快

3、可有序生成

4、可分布式部署

当然缺点也是有的,我相信看了下面SnowflakeUtil的朋友自然能够明白。

下面是SnowflakeUtil类的代码:

package cn.yyjjssnn.utils;

/**

* 本类主要用于生成主键ID,方法参考twitter的SnowFlake。

* SnowFlake的优点是,整体上按照时间自增排序,

* 并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高。

* SnowFlake的示例结构如下(每部分用-分开):

* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000

* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0

* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截得到的值),

* 这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序SnowflakeIdUtil类的twepoch属性)。

* 41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69

* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId,可以合在一起使用,也可以分开使用

* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号

* 加起来刚好64位,为一个Long型。

*/

public class SnowflakeUtil {

/** 序列id所占的位数 支持每毫秒产生1024个id序号 */

private final long sequenceBits = 10L;

/** 机器id所占的位数 支持256台机器 */

private final long workerIdBits = 8L;

/** 区域id所占的位数 支持32个区域(即支持最大机器数为256*32=8192) */

private final long datacenterIdBits = 5L;

/** 开始时间截 (2018-01-01 00:00:00) 可使用至2052年 */

private final long twepoch = 1514736000000L;

/** 机器id左移位 */

private final long workerIdShift = sequenceBits;

/** 区域id左移位 */

private final long datacenterIdShift = sequenceBits + workerIdBits;

/** 时间截左移位 */

private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

/** 生成序列的掩码 */

private final long sequenceMask = -1L ^ (-1L << sequenceBits);

/** 支持的最大机器id */

private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

/** 支持的最大区域id */

private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

/** 当前毫秒内序列 */

private long sequence = 0L;

/** 当前机器id */

private long workerId;

/** 当前区域id */

private long datacenterId;

/** 上次生成ID的时间截 */

private long lastTimestamp = -1L;

/** 一台机子只需要一个实例,以保证产生有序的、不重复的ID */

private static SnowflakeUtil snowflakeUtil = new SnowflakeUtil();

private SnowflakeUtil() {

// 设置workerId和datacenterId

// TODO workerId和datacenterId可以通过数据库、配置文件、缓存等方式获取,这里为方便演示默认都设置为0

long workerId = 0;

long datacenterId = 0;

if (workerId > maxWorkerId || workerId < 0) {

throw new IllegalArgumentException(

String.format("workerId(0~%d)设置错误", maxWorkerId));

}

if (datacenterId > maxDatacenterId || datacenterId < 0) {

throw new IllegalArgumentException(

String.format("datacenterId(0~%d)设置错误", maxDatacenterId));

}

this.workerId = workerId;

this.datacenterId = datacenterId;

}

public static SnowflakeUtil getInstance() {

return snowflakeUtil;

}

/**

* 获得下一个ID (该方法是线程安全的)

* @return SnowflakeId

*/

public synchronized long nextId() {

long timestamp = timeGen();

// 如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常

// 也就是说当应用运行时是不能将时钟改小的,要么异常退出,要么ID重复

if (timestamp < lastTimestamp) {

throw new RuntimeException(String.format("系统时钟回退%d秒", lastTimestamp - timestamp));

}

// 如果是同一时间生成的,则进行毫秒内序列

if (lastTimestamp == timestamp) {

sequence = (sequence + 1) & sequenceMask;

// 毫秒内序列溢出

if (sequence == 0) {

// 阻塞到下一个毫秒,获得新的时间戳

timestamp = tilNextMillis(lastTimestamp);

}

}

// 时间戳改变,毫秒内序列重置

else {

sequence = 0L;

}

// 上次生成ID的时间截

lastTimestamp = timestamp;

// 移位并通过或运算拼到一起组成64位的ID

return ((timestamp - twepoch) << timestampLeftShift) //

| (datacenterId << datacenterIdShift) //

| (workerId << workerIdShift) //

| sequence;

}

/**

* 阻塞到下一个毫秒,直到获得新的时间戳

* @param lastTimestamp 上次生成ID的时间截

* @return 当前时间戳

*/

protected long tilNextMillis(long lastTimestamp) {

long timestamp = timeGen();

while (timestamp <= lastTimestamp) {

timestamp = timeGen();

}

return timestamp;

}

/**

* 返回以毫秒为单位的当前时间

* @return 当前时间(毫秒)

*/

protected long timeGen() {

// 考虑到当前系统时钟不准确以及修改时钟产生的ID问题,

// 这里可以根据自身业务使用网络时钟或其他更加准确及稳定的时钟

return System.currentTimeMillis();

}

}

下面是测试代码:

package cn.yyjjssnn.utils;

import java.util.Hashtable;

import java.util.Map;

public class SnowflakeUtilTest {

public static void main(String[] args) {

// 模拟多线程同时获取ID

for (int i = 0; i < 100; i++) {

new SnowflakeUtilTestThread("线程" + i).start();

}

}

}

class SnowflakeUtilTestThread extends Thread {

private static Map existId = new Hashtable();

public SnowflakeUtilTestThread(String name) {

super(name);

}

@Override

public void run() {

for (int i = 0; i < 1000; i++) {

long id = SnowflakeUtil.getInstance().nextId();

if (existId.containsKey(id + "")) {

throw new RuntimeException("ID重复");

}

existId.put(id + "", id);

System.out.println(getName() + "获取ID:" + id + ",当前ID总数:" + existId.size());

}

}

}

下面是测试结果:

……

线程1获取ID:63578301855170593,当前ID总数:99962

线程73获取ID:63578301855170596,当前ID总数:99965

线程73获取ID:63578301855170598,当前ID总数:99967

线程47获取ID:63578301855170595,当前ID总数:99964

线程73获取ID:63578301855170599,当前ID总数:99968

线程1获取ID:63578301855170597,当前ID总数:99966

线程73获取ID:63578301855170601,当前ID总数:99970

线程73获取ID:63578301855170602,当前ID总数:99971

线程73获取ID:63578301855170603,当前ID总数:99972

线程73获取ID:63578301855170604,当前ID总数:99973

线程73获取ID:63578301855170605,当前ID总数:99974

线程73获取ID:63578301855170606,当前ID总数:99975

线程73获取ID:63578301855170607,当前ID总数:99976

线程73获取ID:63578301855170608,当前ID总数:99977

线程73获取ID:63578301855170609,当前ID总数:99978

……

线程73获取ID:63578301855170626,当前ID总数:99995

线程73获取ID:63578301855170627,当前ID总数:99996

线程73获取ID:63578301855170628,当前ID总数:99997

线程73获取ID:63578301855170629,当前ID总数:99998

线程73获取ID:63578301855170630,当前ID总数:99999

线程73获取ID:63578301863559168,当前ID总数:100000

~谢谢打赏~手机请长按图片~

8ab42301bbc2800528b800f60bb82ad3.png

赞 赏

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/289955.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

云上“炼”码兵器 GitHub Codespaces

GitHub 是全球最受欢迎的开发者平台&#xff0c; ⾃从微软收购了 GitHub 后&#xff0c; GitHub 的功能就越来越强⼤&#xff0c;除了原有的代码管理外&#xff0c;也增加了很多硬货&#xff0c;这就包括了集成 CI/CD 的 GitHub Actions &#xff0c;以及完善的项⽬管理功能&am…

python 中的yum pip

为什么80%的码农都做不了架构师&#xff1f;>>> python 中的yum pip pip 可以管理python中的模块&#xff0c;之于python就像yum之于redhat 转载于:https://my.oschina.net/sskxyz/blog/368099

ArcGIS怎样获取重分类后各类所占的像元个数

【问题描述】如下图所示,给西北地区2007年平均NDVI分等级,求各个等级的面积所占百分比? 解决方法: 刚开始老在如下图所示的Properties界面中进行重分类,虽然能得到分类结果,但是却得不到像元个数,因为是连续的,也不会有属性表生成: 后来发现,工具箱中还有个分类工具…

13.ThreadPoolExecutor线程池之submit方法

jdk1.7.0_79 在上一篇《ThreadPoolExecutor线程池原理及其execute方法》中提到了线程池ThreadPoolExecutor的原理以及它的execute方法。本文解析ThreadPoolExecutor#submit。 对于一个任务的执行有时我们不需要它返回结果&#xff0c;但是有我们需要它的返回执行结果。对于线程…

java idea 模块_idea 多模块项目依赖父工程class找不到问题的方法

比如&#xff0c;我们有这么个过程&#xff0c;项目结构如下&#xff1a;a--b--ca是总结点&#xff0c;b是子节点&#xff0c;c是父节点b依赖父节点class&#xff0c;通过maven构建时通常我们会在子节点中添加父节点依赖&#xff0c;如&#xff1a;com.xxxc${project.version}虽…

关于OAuth2.0 Authorization Code+PKCE flow在原生客户端(Native App)下集成的思考

Working with Proof Key for Code Exchange (PKCE) - DEV Community写在前面前几天看了园友的一篇文章被广泛使用的OAuth2.0的密码模式已经废了&#xff0c;放弃吧 被再次提起&#xff1a;Implicit Flow Password Grant&#xff0c;均已被标记为Legacy&#xff0c;且OAuth2.1里…

【经典回放】多种语言系列数据结构算法:希尔排序(C/C#版)

原理&#xff1a;每隔sp&#xff08;整数&#xff09;个数即取数并判断大小&#xff0c;交换&#xff0c;先构造局部有序序列&#xff0c;直到sp为1&#xff0c;构造完整的有序序列。 给出一组数据&#xff0c;如下&#xff1a; 0 1 2 3 4 5 6 7 8 9 49 38 65 97 …

【131天】尚学堂高淇Java300集视频精华笔记(65-66)

第65集&#xff1a;常用类Date类的使用JDk源码分析 Date时间类&#xff08;java.util.Date&#xff09; 在标准Java类库中包含一个Date类。它的对象表示一个特定的瞬间&#xff0c;精确到毫秒。 Date()分配一个Date对象&#xff0c;并初始化此对象为当前的日期和时间精确到毫秒…

ArcGIS 10.2加载在线影像底图

两种方法&#xff1a; 一、File→ArcGIS Online→影像图→Open。 二、 效果&#xff1a;

都说不要装箱,那装箱到底带来了什么开销?

相信很有朋友在面试时大多会被问到 装箱 的问题&#xff0c;也是一个经典的问题&#xff0c;可深可浅&#xff0c;那今天我们就从 汇编 和 内存 角度进行统一解读下。为了方便演示&#xff0c;先上一段装箱的代码。class Program{static void Main(string[] args){var i 10;va…

Java中方法的继承以及父类未被子类覆盖的方法调用的问题

在看java继承这一块的时候发现了一个问题&#xff0c;即父类未被子类覆盖的方法是如何调用的&#xff1f; 是子类拥有了父类的该方法只是没有显示表示&#xff0c;还是子类调用了父类的该方法。 为此做了一下验证 代码如下&#xff1a; public class One {protected void print…

Excel VBA编程基础

一、程序基本调试方法 1、VB程序的三种状态:设计时、运行时和中断模式。 2、但不执行代码,快捷键为F8。 3、中断程序的方法: (1)使用断点 (2)使用Debug.Assert (3)使用监视条件 二、Excel Shape对象

分治算法之合并排序

1、问题 合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列 2、思路 1、分解:将待排序元素分成大小大致相同的两个子序列 2、治理:对两个子序列进行合并排序 3、…

WPF 实现截屏控件之移动(二)(仿微信)

WPF开发者QQ群此群已满340500857 &#xff0c;请加新群458041663由于微信群人数太多入群请添加小编微信号yanjinhuawechat 或 W_Feng_aiQ 邀请入群需备注WPF开发者 接着上一篇&#xff0c;兼容屏幕缩放问题。01—代码如下一、创建ScreenCut.xaml代码如下。<ResourceDictiona…

深入剖析阿里云推荐引擎——新架构,新体验

摘要&#xff1a;本文的整理自2017云栖大会-上海峰会上阿里云算法专家郑重&#xff08;卢梭&#xff09;的分享讲义&#xff0c;从2016年2月V2.0公开使用到现在&#xff0c;阿里云推荐引擎有了更大的进步。有着获取排序的在线计算&#xff0c;修正匹配的近线计算及匹配排序的离…

分治算法之快速排序

1、快速排序 通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序 2、思路 ( 1 )分解:先从数列中取出一个元素作为基准元素。以基准元素为标准,将问题分解为两个子序列,使小于…

SQL Server 2008空间数据应用系列三:SQL Server 2008空间数据类型

SQL Server 2008空间数据应用系列三&#xff1a;SQL Server 2008空间数据类型 原文:SQL Server 2008空间数据应用系列三&#xff1a;SQL Server 2008空间数据类型友情提示&#xff0c;您阅读本篇博文的先决条件如下&#xff1a; 1、本文示例基于Microsoft SQL Server 2008 R2调…

.NET 很好,你可能对它有一些误解

> 作者&#xff1a;Charles Chen在 20 年前的 2002 年, 微软公布了下一代的软件、服务的愿景和路线&#xff0c;2 月 13 日&#xff0c;Visual Studio .NET 推出&#xff0c;.NET 开发平台的第一个版本正式向世界发布。到现在为止&#xff0c;.NET 都已经 20 岁了, 它已经成…

SQL语言实现金额小写转大写完整案例代码

1. 数字大小写对照表 一到十数字大小写: 1——壹,2——贰,3——叁,4——肆,5——伍,6——陆,7——柒,8——捌,9——玖,10——拾 2. 大小写转换案例 将12.345元转换为大写 select dbo.L2U(12.345,1) select dbo.L2U(123456789.345,1) 结果: 3. SQL转化代码 CREA…

盘点PHP编程常见失误

为什么80%的码农都做不了架构师&#xff1f;>>> 变量声明 如果在一条语句中声明一个变量&#xff0c;如下所示&#xff1a;$varvalue;编译器首先会求出语句右半部分的值&#xff0c;恰恰正是语句的这一部分常常会引发错误。如果使用的语法不正确&#xff0c;就会出…