2015-06-17 47 views
1

我寫過一個計時器,它可以測量任何多線程應用程序中特定代碼的性能。在下面的計時器中,它也將填充地圖,其中有多少個呼叫需要x毫秒。我將使用此圖作爲我的直方圖的部分做進一步的分析,像什麼個電話花了這麼多毫秒等計時器課程中潛在的競爭條件?

public static class StopWatch { 

    public static ConcurrentHashMap<Long, Long> histogram = new ConcurrentHashMap<Long, Long>(); 

    public static StopWatch getInstance() { 
     return new StopWatch(); 
    } 

    private long m_end = -1; 
    private long m_interval = -1; 
    private final long m_start; 

    private StopWatch() { 
     m_start = m_interval = currentTime(); 
    } 

    public long getDuration() { 
     long result = 0; 

     final long startTime = m_start; 
     final long endTime = isStopWatchRunning() ? currentTime() : m_end; 

     result = convertNanoToMilliseconds(endTime - startTime); 

     boolean done = false; 
     while (!done) { 
      Long oldValue = histogram.putIfAbsent(result, 1L); 
      if (oldValue != null) { 
       done = histogram.replace(result, oldValue, oldValue + 1); 
      } else { 
       done = true; 
      } 
     } 

     return result; 
    } 

    public long getInterval() { 
     long result = 0; 

     final long startTime = m_interval; 
     final long endTime; 

     if (isStopWatchRunning()) { 
      endTime = m_interval = currentTime(); 
     } else { 
      endTime = m_end; 
     } 

     result = convertNanoToMilliseconds(endTime - startTime); 

     return result; 
    } 

    public void stop() { 
     if (isStopWatchRunning()) { 
      m_end = currentTime(); 
     } 
    } 

    private long currentTime() { 
     return System.nanoTime(); 
    } 

    private boolean isStopWatchRunning() { 
     return (m_end <= 0); 
    } 

    private long convertNanoToMilliseconds(final long nanoseconds) { 
     return nanoseconds/1000000L; 
    } 
} 

例如,這是我會用我上面的計時器類測量方式特定代碼在我的多線程應用程序的性能:

StopWatch timer = StopWatch.getInstance(); 
//... some code here to measure 
timer.getDuration(); 

現在我的問題是 - 如果你看一下getDuration方法,我也填充我的地圖的信息,如多少電話拍了X毫秒,這樣我可以稍後使用該地圖進行進一步分析,如計算平均值,中位數,第95百分位和第99百分位數。我的下面的代碼線程安全或有任何競爭條件?

boolean done = false; 
while (!done) { 
    Long oldValue = histogram.putIfAbsent(result, 1L); 
    if (oldValue != null) { 
     done = histogram.replace(result, oldValue, oldValue + 1); 
    } else { 
     done = true; 
    } 
} 

調用Long oldValue = histogram.putIfAbsent(result, 1L);done = histogram.replace(result, oldValue, oldValue + 1);之間,在地圖中的值可能已經改變。因此,oldValue可能是陳舊的?

+0

這個話題對於http://codereview.stackexchange .com/ –

+0

@AlexeiKaigorodov只有代碼按預期工作時,OP才能確定。 Broken code is [off-topic](http://codereview.stackexchange.com/help/on-topic)在代碼審查 – Mast

回答

2

您呼出的部分看起來是正確的。是的,有時oldValue會變陳,但這就是爲什麼你要循環。對?

另一種方法是將AtomicLongs放入地圖中。然後你把/得到AtomicLong並增加它。

histogram.putIfAbsent(result, new AtomicLong()); 
histogram.get(result).incrementAndGet(); 

在java中8您可以使用compute和朋友,你的優勢(測試,看看你最喜歡的):

histogram.computeIfAbsent(result, AtomicLong::new); 
histogram.get(result).incrementAndGet(); 

// or 
if (histogram.putIfAbsent(result, new AtomicLong(1)) == null) 
    histogram.get(result).incrementAndGet(); 

// or even 
histogram.compute(result, ($, current) -> { 
    if (current == null) return new AtomicLong(1); 
    current.incrementAndGet(); 
    return current; 
}); 
+0

是的,我的意思是說有沒有其他的方法來做到這一點,而不是像我一樣循環我現在在做什麼? – john

+0

更新了替代 –

+0

'computeIfAbsent'返回有效值,因此它故意允許將操作寫爲單個語句'histogram.computeIfAbsent(result,AtomicLong :: new).incrementAndGet();'而不需要另一個'只讀一次,而不是兩次......即使沒有AtomicLong,新API也可以優化調用:histogram.merge(result,1L,Long :: sum);'這已經是一個原子更新。 – Holger