1. package xxx;
  2.  
  3. import java.sql.Timestamp;
  4. import java.util.concurrent.*;
  5. import java.util.concurrent.atomic.AtomicLong;
  6.  
  7. /**
  8. * 高并发场景下System.currentTimeMillis()的性能问题的优化
  9. * <p><p>
  10. * System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右)<p>
  11. * System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道<p>
  12. * 后台定时更新时钟,JVM退出时,线程自动回收<p>
  13. * 10亿:43410,206,210.72815533980582%<p>
  14. * 1亿:4699,29,162.0344827586207%<p>
  15. * 1000万:480,12,40.0%<p>
  16. * 100万:50,10,5.0%<p>
  17. * @author lry
  18. */
  19. public class SystemClock {
  20. private final long period;
  21. private final AtomicLong now;
  22. ExecutorService executor = Executors.newSingleThreadExecutor();
  23.  
  24. private SystemClock(long period) {
  25. this.period = period;
  26. this.now = new AtomicLong(System.currentTimeMillis());
  27. scheduleClockUpdating();
  28. }
  29.  
  30. private static class InstanceHolder {
  31. public static final SystemClock INSTANCE = new SystemClock(1);
  32. }
  33.  
  34. private static SystemClock instance() {
  35. return InstanceHolder.INSTANCE;
  36. }
  37.  
  38. private void scheduleClockUpdating() {
  39. ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
  40. @Override
  41. public Thread newThread(Runnable runnable) {
  42. Thread thread = new Thread(runnable, "System Clock");
  43. thread.setDaemon(true);
  44. return thread;
  45. }
  46. });
  47. scheduler.scheduleAtFixedRate(new Runnable() {
  48. @Override
  49. public void run() {
  50. now.set(System.currentTimeMillis());
  51. }
  52. }, period, period, TimeUnit.MILLISECONDS);
  53. }
  54.  
  55. private long currentTimeMillis() {
  56. return now.get();
  57. }
  58.  
  59. public static long now() {
  60. return instance().currentTimeMillis();
  61. }
  62.  
  63. public static String nowDate() {
  64. return new Timestamp(instance().currentTimeMillis()).toString();
  65. }
  66. }
  1. package com.zheng.common.util.key;
  2.  
  3. /**
  4. * Twitter_Snowflake<br>
  5. * SnowFlake的结构如下(每部分用-分开):<br>
  6. * 0 - 0000000000 0000000000 0000000000 0000000000 0 - 00000 - 00000 - 000000000000 <br>
  7. * 1位标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负数是1,所以id一般是正数,最高位是0<br>
  8. * 41位时间截(毫秒级),注意,41位时间截不是存储当前时间的时间截,而是存储时间截的差值(当前时间截 - 开始时间截)
  9. * 得到的值),这里的的开始时间截,一般是我们的id生成器开始使用的时间,由我们程序来指定的(如下下面程序IdWorker类的startTime属性)。41位的时间截,可以使用69年,年T = (1L << 41) / (1000L * 60 * 60 * 24 * 365) = 69<br>
  10. * 10位的数据机器位,可以部署在1024个节点,包括5位datacenterId和5位workerId<br>
  11. * 12位序列,毫秒内的计数,12位的计数顺序号支持每个节点每毫秒(同一机器,同一时间截)产生4096个ID序号<br>
  12. * 加起来刚好64位,为一个Long型。<br>
  13. * SnowFlake的优点是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由数据中心ID和机器ID作区分),并且效率较高,经测试,SnowFlake每秒能够产生26万ID左右。
  14. */
  15. public class SnowflakeIdWorker {
  16. // ==============================Fields===========================================
  17. /**
  18. * 开始时间截 (2015-01-01)
  19. */
  20. private final long twepoch = 1420041600000L;
  21.  
  22. /**
  23. * 机器id所占的位数
  24. */
  25. private final long workerIdBits = 5L;
  26.  
  27. /**
  28. * 数据标识id所占的位数
  29. */
  30. private final long datacenterIdBits = 5L;
  31.  
  32. /**
  33. * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数)
  34. */
  35. private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
  36.  
  37. /**
  38. * 支持的最大数据标识id,结果是31
  39. */
  40. private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
  41.  
  42. /**
  43. * 序列在id中占的位数
  44. */
  45. private final long sequenceBits = 12L;
  46.  
  47. /**
  48. * 机器ID向左移12位
  49. */
  50. private final long workerIdShift = sequenceBits;
  51.  
  52. /**
  53. * 数据标识id向左移17位(12+5)
  54. */
  55. private final long datacenterIdShift = sequenceBits + workerIdBits;
  56.  
  57. /**
  58. * 时间截向左移22位(5+5+12)
  59. */
  60. private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
  61.  
  62. /**
  63. * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)
  64. */
  65. private final long sequenceMask = -1L ^ (-1L << sequenceBits);
  66.  
  67. /**
  68. * 工作机器ID(0~31)
  69. */
  70. private long workerId;
  71.  
  72. /**
  73. * 数据中心ID(0~31)
  74. */
  75. private long datacenterId;
  76.  
  77. /**
  78. * 毫秒内序列(0~4095)
  79. */
  80. private long sequence = 0L;
  81.  
  82. /**
  83. * 上次生成ID的时间截
  84. */
  85. private long lastTimestamp = -1L;
  86.  
  87. //==============================Constructors=====================================
  88.  
  89. /**
  90. * 构造函数
  91. *
  92. * @param workerId 工作ID (0~31)
  93. * @param datacenterId 数据中心ID (0~31)
  94. */
  95. public SnowflakeIdWorker(long workerId, long datacenterId) {
  96. if (workerId > maxWorkerId || workerId < 0) {
  97. throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
  98. }
  99. if (datacenterId > maxDatacenterId || datacenterId < 0) {
  100. throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
  101. }
  102. this.workerId = workerId;
  103. this.datacenterId = datacenterId;
  104. }
  105.  
  106. // ==============================Methods==========================================
  107.  
  108. /**
  109. * 获得下一个ID (该方法是线程安全的)
  110. *
  111. * @return SnowflakeId
  112. */
  113. public synchronized long nextId() {
  114. long timestamp = timeGen();
  115.  
  116. //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常
  117. if (timestamp < lastTimestamp) {
  118. throw new RuntimeException(
  119. String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
  120. }
  121.  
  122. //如果是同一时间生成的,则进行毫秒内序列
  123. if (lastTimestamp == timestamp) {
  124. sequence = (sequence + 1) & sequenceMask;
  125. //毫秒内序列溢出
  126. if (sequence == 0) {
  127. //阻塞到下一个毫秒,获得新的时间戳
  128. timestamp = tilNextMillis(lastTimestamp);
  129. }
  130. }
  131. //时间戳改变,毫秒内序列重置
  132. else {
  133. sequence = 0L;
  134. }
  135.  
  136. //上次生成ID的时间截
  137. lastTimestamp = timestamp;
  138.  
  139. //移位并通过或运算拼到一起组成64位的ID
  140. return ((timestamp - twepoch) << timestampLeftShift) //
  141. | (datacenterId << datacenterIdShift) //
  142. | (workerId << workerIdShift) //
  143. | sequence;
  144. }
  145.  
  146. /**
  147. * 阻塞到下一个毫秒,直到获得新的时间戳
  148. *
  149. * @param lastTimestamp 上次生成ID的时间截
  150. * @return 当前时间戳
  151. */
  152. protected long tilNextMillis(long lastTimestamp) {
  153. long timestamp = timeGen();
  154. while (timestamp <= lastTimestamp) {
  155. timestamp = timeGen();
  156. }
  157. return timestamp;
  158. }
  159.  
  160. /**
  161. * 返回以毫秒为单位的当前时间
  162. *
  163. * @return 当前时间(毫秒)
  164. */
  165. protected long timeGen() {
  166. //return System.currentTimeMillis();
  167. return SystemClock.now();
  168. }
  169.  
  170. //==============================Test=============================================
  171.  
  172. /**
  173. * 测试
  174. */
  175. public static void main(String[] args) {
  176. long start = System.currentTimeMillis();
  177. SnowflakeIdWorker idWorker0 = new SnowflakeIdWorker(0, 0);
  178. for (int i = 0; i < 10000000; i++) {
  179. long id = idWorker0.nextId();
  180. //System.out.println(id);
  181. }
  182. System.out.println("耗时:" + (System.currentTimeMillis() - start));
  183. }
  184.  
  185. }

高并发场景下System.currentTimeMillis()的性能问题的优化 以及SnowFlakeIdWorker高性能ID生成器的更多相关文章

  1. 高并发场景下System.currentTimeMillis()的性能问题的优化

    高并发场景下System.currentTimeMillis()的性能问题的优化 package cn.ucaner.alpaca.common.util.key; import java.sql.T ...

  2. 高并发场景下System.currentTimeMillis()的性能优化

    一.前言 System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我也不知道,不过听说在100倍左右),然而该方法又是一个常用方法, 有时不得不使用, ...

  3. Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%。再往后,每提高0.1%,优化难度成指数级增长了。哪怕是千分之一,也直接影响用户体验,影响每天上万张机票的销售额。 在高并发场景下,提供了保证线程安全的对象、方法。比如经典的ConcurrentHashMap,它比起HashMap,有更小粒度的锁,并发读写性能更好。线程安全的StringBuilder取代S

    Qunar机票技术部就有一个全年很关键的一个指标:搜索缓存命中率,当时已经做到了>99.7%.再往后,每提高0.1%,优化难度成指数级增长了.哪怕是千分之一,也直接影响用户体验,影响每天上万张机 ...

  4. HttpClient在高并发场景下的优化实战

    在项目中使用HttpClient可能是很普遍,尤其在当下微服务大火形势下,如果服务之间是http调用就少不了跟http客户端找交道.由于项目用户规模不同以及应用场景不同,很多时候可能不需要特别处理也. ...

  5. C++高并发场景下读多写少的解决方案

    C++高并发场景下读多写少的解决方案 概述 一谈到高并发的解决方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也 ...

  6. C++高并发场景下读多写少的优化方案

    概述 一谈到高并发的优化方案,往往能想到模块水平拆分.数据库读写分离.分库分表,加缓存.加mq等,这些都是从系统架构上解决.单模块作为系统的组成单元,其性能好坏也能很大的影响整体性能,本文从单模块下读 ...

  7. 【转】记录PHP、MySQL在高并发场景下产生的一次事故

    看了一篇网友日志,感觉工作中值得借鉴,原文如下: 事故描述 在一次项目中,上线了一新功能之后,陆陆续续的有客服向我们反应,有用户的个别道具数量高达42亿,但是当时一直没有到证据表示这是,确实存在,并且 ...

  8. MySQL在大数据、高并发场景下的SQL语句优化和"最佳实践"

    本文主要针对中小型应用或网站,重点探讨日常程序开发中SQL语句的优化问题,所谓“大数据”.“高并发”仅针对中小型应用而言,专业的数据库运维大神请无视.以下实践为个人在实际开发工作中,针对相对“大数据” ...

  9. 高并发场景下JVM调优实践之路

    一.背景 2021年2月,收到反馈,视频APP某核心接口高峰期响应慢,影响用户体验. 通过监控发现,接口响应慢主要是P99耗时高引起的,怀疑与该服务的GC有关,该服务典型的一个实例GC表现如下图: 可 ...

随机推荐

  1. vmware配置网卡

    虚拟机网络配置 1. 启用VMWare虚拟网卡 如果没有查看到vmnet8这个网络连接,打开VMWare, 2. 设置虚拟机:选中安装好的虚拟机右键设置. 3. 设置虚拟机系统. 指令:vi /etc ...

  2. spring boot配置德鲁伊

    1.引入相关依赖,全部依赖是上一篇spring boot+mybatis依赖的基础上,再加上下边的依赖,如下: <!-- Druid数据库连接池组件 --> <dependency& ...

  3. IOS-视频

    一.简介 iOS提供了MPMoviePlayerController.MPMoviePlayerViewController两个类,可以用来轻松播放视频和网络流媒体\网络音频 提示:网络音频同样使用此 ...

  4. ActionDescriptor 的认识

    ActionDescriptor的作用是对Action方法的元数据的描述,通过ActionDescriptor我们可以获取到action方法的相关的名称,所属控制器,方法的参数列表,应用到方法上的特性 ...

  5. iOS7.1以后企业应用发布需要HTTPS协议,解决步骤

    操作系统是Window下. 第一步安装软件 1.安装Tomcat6.0.35(Tomcat7.0.34测试通过) 2.安装JDK6(1.6.0_10-rc2,其它版本没测试) 3.安装openssl ...

  6. Redis补充

    Redis补充 (1)redis基本概念 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set ...

  7. Android application testing with the Android test framework

    目录(?)[-] Android automated testing 1 How to test Android applications Tip 2 Unit tests vs functional ...

  8. Qt中QT_BEGIN_NAMESPACE和QT_END_NAMESPACE的作用

    在Qt中,我们经常会看到 QT_BEGIN_NAMESPACE class QAction; class QMenu; class QPlainTextEdit; QT_END_NAMESPACE 这 ...

  9. Markdown速成班

    更多内容请参考: http://ibruce.info/2013/11/26/markdown/

  10. LG2120 [ZJOI2007]仓库建设

    题意 L公司有N个工厂,由高到底分布在一座山上. 工厂1在山顶,工厂N在山脚. 由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用. 突然有一天,L公司的总裁L先生接到 ...