高并发下高效获取时间戳

标签: 并发 时间戳 | 发表时间:2021-04-09 08:10 | 作者:chinotan
出处:https://juejin.cn/tag/%E6%9E%B6%E6%9E%84

背景

Java获取当前时间戳一般是通过System.currentTimeMillis()来获取。这是一个native方法,用于获取当前时间与1970年1月1日0点之间的差,虽然返回值的时间单位是毫秒,但该值的粒度取决于基础操作系统

看下HotSpot源码的hotspot/src/os/linux/vm/os_linux.cpp文件中,有一个javaTimeMillis()方法,这就是System.currentTimeMillis()的native实现

   jlong os::javaTimeMillis() {

timeval time;

int status = gettimeofday(&time, NULL);

assert(status != -1, "linux error");

return jlong(time.tv_sec) * 1000 + jlong(time.tv_usec / 1000);

}
复制代码

可以看到:

  • 调用gettimeofday()需要从用户态切换到内核态;

  • gettimeofday()的表现受Linux系统的计时器(时钟源)影响,在HPET计时器下性能尤其差;

  • 系统只有一个全局时钟源,高并发或频繁访问会造成严重的争用。

HPET计时器性能较差的原因是会将所有对时间戳的请求串行执行。TSC计时器性能较好,因为有专用的寄存器来保存时间戳。缺点是可能不稳定,因为它是纯硬件的计时器,频率可变(与处理器的CLK信号有关)

可以用以下的命令查看时钟源:

   cat /sys/devices/system/clocksource/clocksource0/available_clocksource # 查看全部支持的时钟源
cat /sys/devices/system/clocksource/clocksource0/current_clocksource # 查看当前使用的时钟源
复制代码

修改时钟源:

   echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource
复制代码

解决方案

最常见的办法是用单个调度线程(守护线程)来按毫秒更新时间戳,相当于维护一个全局内存缓存。

其他线程取时间戳时相当于从内存取,不会再造成时钟资源的争用,代价就是牺牲了一些精确度。

代码如下:

   package cn.hutool.core.date;

import java.sql.Timestamp;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 系统时钟    
* 高并发场景下System.currentTimeMillis()的性能问题的优化 * System.currentTimeMillis()的调用比new一个普通对象要耗时的多(具体耗时高出多少我还没测试过,有人说是100倍左右) * System.currentTimeMillis()之所以慢是因为去跟系统打了一次交道 * 后台定时更新时钟,JVM退出时,线程自动回收 */ public class SystemClock { /** 时钟更新间隔,单位毫秒 */ private final long period; /** 现在时刻的毫秒数 */ private volatile long now; /** * 构造 * @param period 时钟更新间隔,单位毫秒 */ public SystemClock(long period) { this.period = period; this.now = System.currentTimeMillis(); scheduleClockUpdating(); } /** * 开启计时器线程 */ private void scheduleClockUpdating() { ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(runnable -> { Thread thread = new Thread(runnable, "System Clock"); thread.setDaemon(true); return thread; }); scheduler.scheduleAtFixedRate(() -> now = System.currentTimeMillis(), period, period, TimeUnit.MILLISECONDS); } /** * @return 当前时间毫秒数 */ private long currentTimeMillis() { return now; } //------------------------------------------------------------------------ static /** * 单例 * */ private static class InstanceHolder { public static final SystemClock INSTANCE = new SystemClock(1); } /** * @return 当前时间 */ public static long now() { return InstanceHolder.INSTANCE.currentTimeMillis(); } /** * @return 当前时间字符串表现形式 */ public static String nowDate() { return new Timestamp(InstanceHolder.INSTANCE.currentTimeMillis()).toString(); } } 复制代码
  • 采用守护线程作为定时任务:守护线程会随着用户线程的结束而结束,无需单独处理关闭问题(程序结束时,守护线程可以立即关闭,不同于用户线程,比如数据库操作线程,正在插入数据的时候,程序结束了,此时必须正常正确的优雅结束插入数据线程),为其他用户线程提供服务支持
  • 定时任务每毫秒调用一次System.currentTimeMillis(),并存入内存中,采用volatile保证不同线程间的可见性(由于采用了volatile所以禁止了重排序,导致CPU三级缓存无法用到,多线程单次调用下可能性能不佳,后面讲到)
  • 单例模式,防止new多个对象,导致起多个定时获取时间戳的守护线程

使用

  • 根据时间戳进行耗时分析或者只是获取当前时间的场景
  • 日志打印(access.log,log4j日志)中日志时间的写入
  • new Date()获取当前时间的场景

其中日志的时间打印

LOG4J篇:

我们采用Lombok的日志注解,启动一个单元测试,走一下看看我们平常用的日志框架是如何注入时间戳的

如上图,进入断点,选择步入方法(进入方法内部)

接着步入

如上图,选择步过(跳过其他方法,进入该方法的下一步)

如上图,选择步入后,选择要步入的方法

接着按照次方法一步一步走......

走到上图这里后,我们发下这边会返回一个logEvent,这个logEvent涉及到时间戳

所以这个logEventFactory对象是怎么初始化的就很重要

如上图,我们当前类搜索logEventFactory,结合构造函数已经静态类,可以看到这个logEnvetFactory工厂类是通过log4j的配置文件log4j2.component.properties获取,进行初始化

如下图,看下默认的LogEvent工厂

如下图,可以看到纳秒时间的获取默认是获取一个假的时间,时间戳是0,无需关注

我们需要关注CLOCK这个对象的获取(通过工厂类获取)

我们看下getStringProperty方法,如下图,通过environment的get获取,我们来看下这个get方法

如上图,我们可以看到,除了在配置文件(log4j2.component.properties)外,这个属性的key(log4j.Clock)是可以通过java启动变量来注入的,类似 -Dlog4j.Clock=自定义的类路径

之后,我们需要实现Clock的currentTimeMillis接口,如下图

如下图,只需实现这个接口就好,之后把这个类路径注入到启动参数中,log4j就会用这个获取时间的方式

   public class SystemLogClock implements Clock {

  @Override
  public long currentTimeMillis() {
    // 改为从内存获取
    return SystemClock.now();
  }
}
复制代码

除此之外就没有了吗,当然不是,还需要注意一个地方,我们接着之前LoggerConfig的断点往下走,如下图

如上图,看到Layout,大家应该不陌生,通过继承AbstractStringLayout实现toSerializable方法来进行格式化输出。

此外还需要 @Plugin注解进行注册

   @Plugin(name = "CustomLayout", category = Node.CATEGORY, elementType = Layout.ELEMENT_TYPE, printObject = true)
复制代码

还需要在log4j2-dev.xml这样的配置文件中进行注入,类似

   











复制代码

在自定义的CustomLayout中实现toSerializable时,我们可以使用从内存中获取的时间戳进行赋值

ACCESS.LOG篇:

同log4j的分析,我们需要打断点来分析注入时间戳的地方,以undertow为例

我们知道undertow有个配置文件的地方,类似:

   server:
  port: 8888
  undertow:
    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
    # 每块buffer的空间大小,越小的空间被利用越充分
    buffer-size: 1024
    # 是否分配的直接内存
    direct-buffers: true
    accesslog:
      dir: "/logs"
      enabled: true
      pattern: '%h %l %u %t "%r" %s %b'
      prefix: "custom.access.log"
      suffix: ""
    threads:
      worker: 4096
复制代码

观察上图的pattern,我们注意到有个%t 这个占位符,这个正好对应请求的时间戳的位置,类似

   0:0:0:0:0:0:0:1 - - 2021-04-08 16:01:21.867 "GET /custom?token=aaaa HTTP/1.1" 200 1316
复制代码

那么可以自定义一个占位符来更改获取时间戳的逻辑

结合我的上一篇文章 Undertow容器在Springboot中如何自定义修改文件名  juejin.cn/post/693060… 可以知道修改获取时间戳的地方

我们只需要继承ExchangeAttribute接口,实现readAttribute方法就可以

再来看下builders的获取,如下图:

同样我们可以参照io.undertow.attribute.DateTimeAttribute 来进行重写,修改时间戳的获取方式,并自定义占位符前缀 %{customTime, 如下图:

   package com.xiaoju.manhattan.messenger.notice.api.config.undertow;

import cn.hutool.core.date.SystemClock;
import io.undertow.attribute.ExchangeAttribute;
import io.undertow.attribute.ExchangeAttributeBuilder;
import io.undertow.attribute.ReadOnlyAttributeException;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.DateUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;

/**
 * The current time
 *
 * @author Stuart Douglas
 */
public class CustomDateTimeAttribute implements ExchangeAttribute {

  public static final String CUSTOM_TIME = "%{customTime,";

  public static final ExchangeAttribute INSTANCE = new CustomDateTimeAttribute();

  private final String dateFormat;
  private final ThreadLocal cachedFormat;

  private CustomDateTimeAttribute() {
    this.dateFormat = null;
    this.cachedFormat = null;
  }

  public CustomDateTimeAttribute(final String dateFormat) {
    this(dateFormat, null);
  }

  public CustomDateTimeAttribute(final String dateFormat, final String timezone) {
    this.dateFormat = dateFormat;
    this.cachedFormat = ThreadLocal.withInitial(() -> {
      final SimpleDateFormat format = new SimpleDateFormat(dateFormat);
      if (timezone != null) {
        format.setTimeZone(TimeZone.getTimeZone(timezone));
      }
      return format;
    });
  }

  @Override
  public String readAttribute(final HttpServerExchange exchange) {
    if (dateFormat == null) {
      return DateUtils.toCommonLogFormat(new Date(SystemClock.now()));
    } else {
      final SimpleDateFormat dateFormat = this.cachedFormat.get();
      return dateFormat.format(new Date(SystemClock.now()));
    }
  }

  @Override
  public void writeAttribute(final HttpServerExchange exchange, final String newValue)
      throws ReadOnlyAttributeException {
    throw new ReadOnlyAttributeException("Date time", newValue);
  }

  public static final class Builder implements ExchangeAttributeBuilder {

    @Override
    public String name() {
      return "Date Time";
    }

    @Override
    public ExchangeAttribute build(final String token) {
      if (token.startsWith(CUSTOM_TIME) && token.endsWith("}")) {
        return new CustomDateTimeAttribute(
            token.substring(CUSTOM_TIME.length(), token.length() - 1));
      }
      return null;
    }

    @Override
    public int priority() {
      return 0;
    }
  }
}
复制代码

接着仿照SPI的方式,进行注入,如下图

   custom.undertow.CustomDateTimeAttribute$Builder
复制代码

最后修改下我们的pattern,更改%t 为 %{customTime,yyyy-MM-dd HH:mm:ss.SSS}

   pattern: '%h %l %u %{customTime,yyyy-MM-dd HH:mm:ss.SSS} "%r" %s %b'
复制代码

这样就可以实现时间戳获取的替换

性能测试

为了测试性能,写了一个单元测试

   package custom;

import cn.hutool.core.date.SystemClock;
import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;
import java.util.concurrent.CompletableFuture;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

/**
 * @Description 系统时钟并发测试
 */
@Slf4j
public class SystemClockTest {


  /**
   * 起2000个线程进行获取当前时间戳(native调用)
   */
  @Test
  @JunitPerfConfig(threads = 2000)
  public void testSystemTime() {
    System.currentTimeMillis();
  }

  /**
   * 起2000个线程进行获取当前时间戳(内存调用)
   */
  @Test
  @JunitPerfConfig(threads = 2000)
  public void testSystemClockMultiThread() {
    SystemClock.now();
  }

  /**
   * 测试log4j日志写入流程
   */
  @Test
  public void testLog() {
    log.info("欧拉欧拉欧拉欧拉欧拉欧拉欧拉欧拉");
  }

  @Test
  public void systemClockTest() throws InterruptedException {
    // 预热
    long ready = SystemClock.now();
    int num = 10000;
    long start0 = System.currentTimeMillis();
    batchExe(num, () -> {
      for (int i = 0; i < 10; i++) {
        SystemClock.now();
      }
    });
    long end0 = System.currentTimeMillis();
    System.out.println("pre ready SystemClock Time:" + (end0 - start0) + "毫秒");

    // SystemClock
    long start1 = System.currentTimeMillis();
    batchExe(num, () -> {
      for (int i = 0; i < num; i++) {
        SystemClock.now();
      }
    });
    long end1 = System.currentTimeMillis();
    System.out.println("SystemClock Time:" + (end1 - start1) + "毫秒");

    // currentTimeMillis
    long start2 = System.currentTimeMillis();
    batchExe(num, () -> {
      for (int i = 0; i < num; i++) {
        System.currentTimeMillis();
      }
    });
    long end2 = System.currentTimeMillis();
    System.out.println("CurrentTimeMillis Time:" + (end2 - start2) + "毫秒");
  }

  private void batchExe(int num, Runnable runnable) {
    CompletableFuture[] futures = new CompletableFuture[num];
    for (int i = 0; i < num; i++) {
      futures[i] = CompletableFuture.runAsync(runnable);
    }

    CompletableFuture.allOf(futures).join();
  }

}
复制代码

当有1万个线程并发调用,每个线程获取一万次时间戳的时候(进行预热,排除对象初始化,线程初始化占用的时间),如下图:

可以看到有明细的性能差距,大概有10倍的差距

当有50个线程直接调用时(native调用):

   [INFO] [2021-04-02 17:17:48.301] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - --------------------------------------------------------
[INFO] [2021-04-02 17:17:48.303] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Started at:  2021-04-02 17:16:48.177
[INFO] [2021-04-02 17:17:48.303] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Invocations:  67108914
[INFO] [2021-04-02 17:17:48.304] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Success:  67108914
[INFO] [2021-04-02 17:17:48.304] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Errors:  0
[INFO] [2021-04-02 17:17:48.304] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Thread Count:  50
[INFO] [2021-04-02 17:17:48.305] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Warm up:  0ms
[INFO] [2021-04-02 17:17:48.305] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Execution time:  60000ms
[INFO] [2021-04-02 17:17:48.305] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Throughput:  1118481/s (Required: -1/s) - PASSED
[INFO] [2021-04-02 17:17:48.305] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Memory cost:  16byte
[INFO] [2021-04-02 17:17:48.428] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Min latency:  1.0E-6ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:17:48.512] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Max latency:  272.46924ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:17:48.683] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Avg latency:  1.922781E-4ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:17:48.683] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - --------------------------------------------------------
复制代码

当有50个线程直接调用时(内存调用)

   [INFO] [2021-04-02 17:19:08.016] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - --------------------------------------------------------
[INFO] [2021-04-02 17:19:08.017] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Started at:  2021-04-02 17:18:07.834
[INFO] [2021-04-02 17:19:08.017] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Invocations:  67108914
[INFO] [2021-04-02 17:19:08.017] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Success:  67108914
[INFO] [2021-04-02 17:19:08.018] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Errors:  0
[INFO] [2021-04-02 17:19:08.018] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Thread Count:  50
[INFO] [2021-04-02 17:19:08.018] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Warm up:  0ms
[INFO] [2021-04-02 17:19:08.019] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Execution time:  60000ms
[INFO] [2021-04-02 17:19:08.019] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Throughput:  1118481/s (Required: -1/s) - PASSED
[INFO] [2021-04-02 17:19:08.019] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Memory cost:  16byte
[INFO] [2021-04-02 17:19:08.112] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Min latency:  1.0E-6ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:19:08.196] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Max latency:  179.08081ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:19:08.368] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Avg latency:  1.3518152E-4ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:19:08.369] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - --------------------------------------------------------
复制代码

但是当1万个线程并发调用,每个线程获取一次时间戳的时候,如下图:

当有2000个线程直接调用时(native调用)

   [INFO] [2021-04-02 17:30:48.815] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - --------------------------------------------------------
[INFO] [2021-04-02 17:30:48.816] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Started at:  2021-04-02 17:27:32.223
[INFO] [2021-04-02 17:30:48.817] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Invocations:  67110864
[INFO] [2021-04-02 17:30:48.817] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Success:  67110864
[INFO] [2021-04-02 17:30:48.817] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Errors:  0
[INFO] [2021-04-02 17:30:48.817] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Thread Count:  2000
[INFO] [2021-04-02 17:30:48.817] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Warm up:  0ms
[INFO] [2021-04-02 17:30:48.818] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Execution time:  60000ms
[INFO] [2021-04-02 17:30:48.818] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Throughput:  1118514/s (Required: -1/s) - PASSED
[INFO] [2021-04-02 17:30:48.818] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Memory cost:  16byte
[INFO] [2021-04-02 17:30:48.926] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Min latency:  1.0E-6ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:30:49.028] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Max latency:  99.11504ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:30:49.209] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - Avg latency:  1.8578941E-4ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:30:49.209] [c.x.m.m.n.a.s.SystemClockTest.testSystemTime] - 
复制代码

当有2000个线程直接调用时(内存调用)

   [INFO] [2021-04-02 17:34:26.853] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - --------------------------------------------------------
[INFO] [2021-04-02 17:34:26.856] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Started at:  2021-04-02 17:31:14.774
[INFO] [2021-04-02 17:34:26.856] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Invocations:  67110864
[INFO] [2021-04-02 17:34:26.856] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Success:  67110864
[INFO] [2021-04-02 17:34:26.856] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Errors:  0
[INFO] [2021-04-02 17:34:26.857] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Thread Count:  2000
[INFO] [2021-04-02 17:34:26.857] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Warm up:  0ms
[INFO] [2021-04-02 17:34:26.858] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Execution time:  60000ms
[INFO] [2021-04-02 17:34:26.858] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Throughput:  1118514/s (Required: -1/s) - PASSED
[INFO] [2021-04-02 17:34:26.859] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Memory cost:  16byte
[INFO] [2021-04-02 17:34:26.991] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Min latency:  1.0E-6ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:34:27.089] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Max latency:  167.16472ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:34:27.293] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - Avg latency:  3.9316338E-4ms (Required: -1.0ms) - PASSED
[INFO] [2021-04-02 17:34:27.293] [c.x.m.m.n.a.s.SystemClockTest.testSystemClockMultiThread] - 
复制代码

可以看到,从内存获取反而不如直接native调用速度快

原因是:通过volatile修饰的变量,无法重排序,也无法使用写缓冲和三级缓存,为了保证可见性,只能通过主内存来获取

我们知道三级缓存是SRAM缓存,速度要比内存缓存快很多,一级缓存的话,平均速度是1ns,三级缓存也有12ns左右,而内存速度要差很多

所以多线程调用下,可能还不如直接的系统调用,例如TSC计时器性能较好,因为有专用的寄存器来保存时间戳

注意事项

  • 在System.currentTimeMillis()的效率没有影响程序整体的效率时,就完全没有必要做这种优化,这只是为极端情况准备的
  • 如果要改造获取时间戳的方式,那么日志,业务部分等的地方都需要替换下

相关 [并发 时间戳] 推荐:

高并发下高效获取时间戳

- - 掘金 架构
Java获取当前时间戳一般是通过System.currentTimeMillis()来获取. 这是一个native方法,用于获取当前时间与1970年1月1日0点之间的差,虽然返回值的时间单位是毫秒,但该值的粒度取决于基础操作系统. 看下HotSpot源码的hotspot/src/os/linux/vm/os_linux.cpp文件中,有一个javaTimeMillis()方法,这就是System.currentTimeMillis()的native实现.

SeaJS 里版本号和时间戳管理的最佳实践

- 别致 - 岁月如歌
用 seajs 组织项目,上线后,经常需要更新特定文件或所有文件的时间戳,以清空浏览器缓存. 这种方式很简单直观,弊端也很明显:文件一多,时间戳会分散在各个文件,维护起来不方便. 第二种方式是利用 alias:. 这种方式用来维护 jquery 等类库模块的版本号是非常方便的. 但用来加时间戳,文件一多时,依旧不方便.

让protobuf生成器支持时间戳检查

- Ease - C++博客-首页原创精华区
使用protobuf的生成器可以对proto文件进行解析后生成指定的目标语言代码.随着项目的不断扩大, 协议修改变的非常频繁, 因此每次重编变的异常耗时. 模仿C/C++编译器的时间戳检查生成机制,我给protobuf生成器添加了时间戳检查功能. 此功能不是必须的, 可以通过命令行指定—timestampfile FILE 来指定要生成的proto文件对应的时间戳.

js时间戳怎么转成日期格式

- - CSDN博客推荐文章
js时间戳怎么转成日期格式. 这个在主群里有朋友§☆釺哖蟲...o问js时间戳怎么转成日期格式 ,他的问题是这样的. /Date(1354116249000)/ 这样的格式怎么转成时间格式. 这是从C#的Datatime格式通过Json传到Js里面的,. js需要把时间戳转为为普通格式,一般的情况下可能用不到的,.

ORACLE----TimeStamp(时间戳)字段自动更新

- -
之前对id的自增长进行了设置,原以为时间戳的更新也类似,使用序列之类的,但最终发现跟序列没半毛钱关系. 触发器是肯定要用的,但编写脚本也是需要的. 图2中,我们要选择事件(即例子中的INSERT,UPDATE),触发时间(即INSERT,UPDATE事件的前或后,例子中是before),列(例子中为MODIFIED_DATE,为TimeStamp类型),新旧值(为系统默认值OLD和NEW).

高并发

- - 开源软件 - ITeye博客
垂直扩展是一种用于增加单个ActiveMQ代理连接数(因而也增加了负载能力)的技术.默认情况下,. ActiveMQ的被设计成尽可高效的传输消息以确保低延迟和良好的性能. 默认情况下,ActiveMQ使用阻塞IO来处理传输连接,这种方式为每一个连接分配一个线程. 你可以为ActiveMQ代理使用非阻塞IO(同时客户端可以使用默认的传输)以减少线程的使用.

并发导论

- - 并发编程网 - ifeve.com
由于之前工作中的疏忽,在使用Java多线程并发的时候出了问题,遂决心全面学习并发相关知识. 写作本文的意图只是希望在写作过程中把想不清楚或是一时无法掌握的地方反复揣摩记录下来. 写作本文参考的各种资料较多,抱歉的是文末的参考文献中对一些叫不上名字或没有出处的资料文献并未列举出来. 由于本人是初入职场的菜鸟,更是并发的门外汉,文中关于并发以及其他软硬件、程序设计语言的论据也许不够客观甚至不够正确.

J.U.C并发框架

- - 并发编程网 - ifeve.com
作者:Doug Lea. 在J2SE1.5中,java.util.concurrent包下的大部分同步工具(锁、屏障等)以AbstractQueuedSynchronizer类为基础来构建. 这个框架提供了一些常用机制用于自动管理并发状态、阻塞及非阻塞线程,以及队列. 本论文描述了该框架的根源、设计、实现、用法及性能.

高并发---限流

- - Java - 编程语言 - ITeye博客
在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流. 缓存的目的是提升系统访问速度和增大系统能处理的容量,可谓是抗高并发流量的银弹;而降级是当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;而有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页),因此需有一种手段来限制这些场景的并发/请求量,即限流.

tomcat 高并发优化

- - 企业架构 - ITeye博客
maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200. minSpareThreads 表示空闲的线程数,据我的理解,类似于连接池. acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100.    另外,有时候,还需要调整jvm的启动参数.