高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器
package xxx; import java.sql.Timestamp;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong; /**
* 高并发场景下System.currentTimeMillis()的性能问题的优化
* <p><p>
* System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右)<p>
* System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道<p>
* 后台定时更新时钟,JVM退出时,线程自动回收<p>
* 10亿:43410,206,210.72815533980582%<p>
* 1亿:4699,29,162.0344827586207%<p>
* 1000万:480,12,40.0%<p>
* 100万:50,10,5.0%<p>
* @author lry
*/
public class SystemClock {
private final long period;
private final AtomicLong now;
ExecutorService executor = Executors.newSingleThreadExecutor(); private SystemClock(long period) {
this.period = period;
this.now = new AtomicLong(System.currentTimeMillis());
scheduleClockUpdating();
} private static class InstanceHolder {
public static final SystemClock INSTANCE = new SystemClock(1);
} private static SystemClock instance() {
return InstanceHolder.INSTANCE;
} private void scheduleClockUpdating() {
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable, "System Clock");
thread.setDaemon(true);
return thread;
}
});
scheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
now.set(System.currentTimeMillis());
}
}, period, period, TimeUnit.MILLISECONDS);
} private long currentTimeMillis() {
return now.get();
} public static long now() {
return instance().currentTimeMillis();
} public static String nowDate() {
return new Timestamp(instance().currentTimeMillis()).toString();
}
}
package com.zheng.common.util.key; /**
* Twitter_Snowflake<br>
* SnowFlake的结构如下(每部分用-分开):<br>
* 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
* 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
* 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
* 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
* 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
* 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
* 加起来刚好64位,为一个Long型。<br>
* SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
*/
public class SnowflakeIdWorker {
// ==============================Fields===========================================
/**
* 开始时间截 (2015-01-01)
*/
private final long twepoch = 1420041600000L; /**
* 机器id所占的位数
*/
private final long workerIdBits = 5L; /**
* 数据标识id所占的位数
*/
private final long datacenterIdBits = 5L; /**
* 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
*/
private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /**
* 支持的最大数据标识id,结果是31
*/
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); /**
* 序列在id中占的位数
*/
private final long sequenceBits = 12L; /**
* 机器ID向左移12位
*/
private final long workerIdShift = sequenceBits; /**
* 数据标识id向左移17位(12+5)
*/
private final long datacenterIdShift = sequenceBits + workerIdBits; /**
* 时间截向左移22位(5+5+12)
*/
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; /**
* 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
*/
private final long sequenceMask = -1L ^ (-1L << sequenceBits); /**
* 工作机器ID(0~31)
*/
private long workerId; /**
* 数据中心ID(0~31)
*/
private long datacenterId; /**
* 毫秒内序列(0~4095)
*/
private long sequence = 0L; /**
* 上次生成ID的时间截
*/
private long lastTimestamp = -1L; //==============================Constructors===================================== /**
* 构造函数
*
* @param workerId 工作ID (0~31)
* @param datacenterId 数据中心ID (0~31)
*/
public SnowflakeIdWorker(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
} // ==============================Methods========================================== /**
* 获得下一个ID (该方法是线程安全的)
*
* @return SnowflakeId
*/
public synchronized long nextId() {
long timestamp = timeGen(); //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
if (timestamp < lastTimestamp) {
throw new RuntimeException(
String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", 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() {
//return System.currentTimeMillis();
return SystemClock.now();
} //==============================Test============================================= /**
* 测试
*/
public static void main(String[] args) {
long start = System.currentTimeMillis();
SnowflakeIdWorker idWorker0 = new SnowflakeIdWorker(0, 0);
for (int i = 0; i < 10000000; i++) {
long id = idWorker0.nextId();
//System.out.println(id);
}
System.out.println("耗时:" + (System.currentTimeMillis() - start));
} }
高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器的更多相关文章
- 高并发场景下System.currentTimeMillis()的性能问题的优化
高并发场景下System.currentTimeMillis()的性能问题的优化 package cn.ucaner.alpaca.common.util.key; import java.sql.T ...
- 高并发场景下System.currentTimeMillis()的性能优化
一.前言 System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我也不知道,不过听说在100倍左右),然而该方法又是一个常用方法, 有时不得不使用, ...
- Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S
Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...
- HttpClient在高并发场景下的优化实战
在项目中使用HttpClient可能是很普遍,尤其在当下微服务大火形势下,如果服务之间是http调用就少不了跟http客户端找交道.由于项目用户规模不同以及应用场景不同,很多时候可能不需要特别处理也. ...
- C++高并发场景下读多写少的解决方案
C++高并发场景下读多写少的解决方案 概述 一谈到高并发的解决方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也 ...
- C++高并发场景下读多写少的优化方案
概述 一谈到高并发的优化方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也能很大的影响整体性能,本文从单模块下读 ...
- 【转】记录PHP、MySQL在高并发场景下产生的一次事故
看了一篇网友日志,感觉工作中值得借鉴,原文如下: 事故描述 在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达42亿,但是当时一直没有到证据表示这是,确实存在,并且 ...
- MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"
本文主要针对中小型应用或网站,重点探讨日常程序开发中SQL语句的优化问题,所谓“大数据”.“高并发”仅针对中小型应用而言,专业的数据库运维大神请无视.以下实践为个人在实际开发工作中,针对相对“大数据” ...
- 高并发场景下JVM调优实践之路
一.背景 2021年2月,收到反馈,视频APP某核心接口高峰期响应慢,影响用户体验. 通过监控发现,接口响应慢主要是P99耗时高引起的,怀疑与该服务的GC有关,该服务典型的一个实例GC表现如下图: 可 ...
随机推荐
- 关于绑定的C#代码+转换器
BindingOperations.SetBinding( exp, Expander.IsExpandedProperty, new Bi ...
- 5.8 页面对象(Page Object)模式
页面对象(Page Object)模式是目前自动化测试领域普遍使用的设计模式之一,此模式可以大大提高测试代码的复用率,提高测试脚本的编写效率和维护效率,是中级自动化测试工程师的必备技能之一. 1.页面 ...
- 方便处理hosts的批处理脚本:hosts助手.bat
hosts助手.bat @echo off pushd "%~dp0" set sp0=------------------ set sp1=hosts助手 set hostsfi ...
- react 总结
1.React 里直接修改 this.state 和调用 setState() 修改 state 的值有什么区别? 使用对this.state赋值并没有什么作用,官方提醒,应该把this.state当 ...
- 下载并安装Prism5.0库(纯汉语版)
Prism5.0中包含了文档,WPF代码示例,程序集.本篇告诉你从哪里获取程序集和代码示例,还有NuGet包的内容. 对于新功能,资产,和API的更改信息,请看Prism5.0新内容. 文档 Pris ...
- Unity3D事件顺序与功能
Unity3D中所有控制脚本的基类MonoBehaviour有一些虚函数用于绘制中事件的回调,也可以直接理解为事件函数,例如大家都很清楚的Start,Update等函数,以下做个总结. Awake 当 ...
- webpack4工具链升级排坑记录
1.webpack4号称是0配置,于是我就只设置了entry.resolve.output.module->rules之类的属性,结果通过webpack-bundle-analyzer跑出来发现 ...
- poi自定义颜色设置(转)
原文链接 http://javapolo.iteye.com/blog/1604501 最近在项目的开发中使用到了apache poi,该组件可以让我们方便的操作excell,该工具非常容易上手,但 ...
- 浅析Java基本数据类型
作为一种强类型语言,Java针对每一种数据都定义了明确的数据类型.大体来讲可分为:基本数据类型和引用数据类型:在此,主要讨论前者,后者以后再继续探究. Java基本数据类型分为四类八种: 1: 整数 ...
- Android 进阶11:进程通信之 ContentProvider 内容提供者
学习启舰大神,每篇文章写一句励志的话,与大家共勉. When you are content to be simply yourself and don't compare or compete, e ...