<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          用System.currentTimeMillis() 竟然遇到性能問(wèn)題,有點(diǎn)不相信~

          共 7146字,需瀏覽 15分鐘

           ·

          2021-05-29 22:22

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          真香!24W字的Java面試手冊(cè)(點(diǎn)擊查看)

          獲取當(dāng)前時(shí)間戳,System.currentTimeMillis()是極其常用的基礎(chǔ)

          Java API,廣泛地用來(lái)獲取時(shí)間戳或測(cè)量代碼執(zhí)行時(shí)長(zhǎng)等,在我們的印象中應(yīng)該快如閃電。但實(shí)際上在并發(fā)調(diào)用或者特別頻繁調(diào)用它的情況下(比如一個(gè)業(yè)務(wù)繁忙的接口,或者吞吐量大的需要取得時(shí)間戳的流式程序),其性能表現(xiàn)會(huì)令人大跌眼鏡。

          直接看下面的Demo

          public class CurrentTimeMillisPerfDemo {
              private static final int COUNT = 100;

              public static void main(String[] args) throws Exception {
                  long beginTime = System.nanoTime();
                  for (int i = 0; i < COUNT; i++) {
                      System.currentTimeMillis();
                  }

                  long elapsedTime = System.nanoTime() - beginTime;
                  System.out.println("100 System.currentTimeMillis() serial calls: " + elapsedTime + " ns");

                  CountDownLatch startLatch = new CountDownLatch(1);
                  CountDownLatch endLatch = new CountDownLatch(COUNT);
                  for (int i = 0; i < COUNT; i++) {
                      new Thread(() -> {
                          try {
                              startLatch.await();
                              System.currentTimeMillis();
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          } finally {
                              endLatch.countDown();
                          }
                      }).start();
                  }

                  beginTime = System.nanoTime();
                  startLatch.countDown();
                  endLatch.await();
                  elapsedTime = System.nanoTime() - beginTime;
                  System.out.println("100 System.currentTimeMillis() parallel calls: " + elapsedTime + " ns");
              }
          }

          執(zhí)行結(jié)果如下圖。

          可見(jiàn),并發(fā)調(diào)用System.currentTimeMillis()一百次,耗費(fèi)的時(shí)間是單線程調(diào)用一百次的250倍。如果單線程的調(diào)用頻次增加(比如達(dá)到每毫秒數(shù)次的地步),也會(huì)觀察到類似的情況。實(shí)際上在極端情況下,System.currentTimeMillis()的耗時(shí)甚至?xí)葎?chuàng)建一個(gè)簡(jiǎn)單的對(duì)象實(shí)例還要多,看官可以自行將上面線程中的語(yǔ)句換成new HashMap<>之類的試試看。

          為什么會(huì)這樣呢?來(lái)到HotSpot源碼的hotspot/src/os/linux/vm/os_linux.cpp文件中,有一個(gè)javaTimeMillis()方法,這就是System.currentTimeMillis()的native實(shí)現(xiàn)。

          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);
          }

          挖源碼就到此為止,因?yàn)橐呀?jīng)有國(guó)外大佬深入到了匯編的級(jí)別來(lái)探究,詳情可以參見(jiàn)《The Slow currentTimeMillis()》這篇文章,我就不班門(mén)弄斧了。

          http://pzemtsov.github.io/2017/07/23/the-slow-currenttimemillis.html

          簡(jiǎn)單來(lái)講就是:

          HPET計(jì)時(shí)器性能較差的原因是會(huì)將所有對(duì)時(shí)間戳的請(qǐng)求串行執(zhí)行。TSC計(jì)時(shí)器性能較好,因?yàn)橛袑S玫募拇嫫鱽?lái)保存時(shí)間戳。缺點(diǎn)是可能不穩(wěn)定,因?yàn)樗羌冇布挠?jì)時(shí)器,頻率可變(與處理器的CLK信號(hào)有關(guān))。

          另外,可以用以下的命令查看和修改時(shí)鐘源。

          ~ cat /sys/devices/system/clocksource/clocksource0/available_clocksource
          tsc hpet acpi_pm
          ~ cat /sys/devices/system/clocksource/clocksource0/current_clocksource
          tsc
          echo 'hpet' > /sys/devices/system/clocksource/clocksource0/current_clocksource

          如何解決這個(gè)問(wèn)題?最常見(jiàn)的辦法是用單個(gè)調(diào)度線程來(lái)按毫秒更新時(shí)間戳,相當(dāng)于維護(hù)一個(gè)全局緩存。其他線程取時(shí)間戳?xí)r相當(dāng)于從內(nèi)存取,不會(huì)再造成時(shí)鐘資源的爭(zhēng)用,代價(jià)就是犧牲了一些精確度。具體代碼如下。

          public class CurrentTimeMillisClock {
              private volatile long now;

              private CurrentTimeMillisClock() {
                  this.now = System.currentTimeMillis();
                  scheduleTick();
              }

              private void scheduleTick() {
                  new ScheduledThreadPoolExecutor(1, runnable -> {
                      Thread thread = new Thread(runnable, "current-time-millis");
                      thread.setDaemon(true);
                      return thread;
                  }).scheduleAtFixedRate(() -> {
                      now = System.currentTimeMillis();
                  }, 11, TimeUnit.MILLISECONDS);
              }

              public long now() {
                  return now;
              }
              
              public static CurrentTimeMillisClock getInstance() {
                  return SingletonHolder.INSTANCE;
              }

              private static class SingletonHolder {
                  private static final CurrentTimeMillisClock INSTANCE = new CurrentTimeMillisClock();
              }
          }

          使用的時(shí)候,直接CurrentTimeMillisClock.getInstance().now()就可以了。不過(guò),在System.currentTimeMillis()的效率沒(méi)有影響程序整體的效率時(shí),就不必忙著做優(yōu)化,這只是為極端情況準(zhǔn)備的。

          來(lái)源 | https://urlify.cn/qyu2eu

          如有文章對(duì)你有幫助,

          歡迎關(guān)注??、點(diǎn)贊??、轉(zhuǎn)發(fā)??!



          推薦 Java面試手冊(cè) 
          內(nèi)容包括網(wǎng)絡(luò)協(xié)議、Java基礎(chǔ)、進(jìn)階、字符串、集合、并發(fā)、JVM、數(shù)據(jù)結(jié)構(gòu)、算法、MySQL、Redis、Mongo、Spring、SpringBoot、MyBatis、SpringCloud、Linux以及各種中間件(Dubbo、Nginx、Zookeeper、MQ、Kafka、ElasticSearch)等等...

          點(diǎn)擊文末“閱讀原文”可直達(dá)

          瀏覽 46
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲天堂无码在线观看 | 狠狠狠狠狠狠狠狠狠操 | 综合草逼网| 一级学生妹毛片 | 欧美高清日韩 |