2015-06-17 2 views
1

Я написал таймер, который будет измерять производительность конкретного кода в любом многопоточном приложении. В нижнем таймере он также заполнит карту тем, сколько звонков заняло х миллисекунд. Я буду использовать эту карту как часть моей гистограммы для дальнейшего анализа, как и какой процент звонков приняло это много миллисекунды и т.д.Потенциальное состояние гонки в классе таймера?

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, я также заселять свою карту с информацией, например, сколько звонков приняли е миллисекунды, так что я может использовать эту карту позже для дальнейшего анализа, например, вычисления среднего, среднего, 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 не уверен. Сломанный код [off-topic] (http://codereview.stackexchange.com/help/on-topic) в Code Review – Mast

ответ

2

Часть, которую вы вызвали, выглядит правильно. Да иногда oldValue будет устаревшим, но именно поэтому вы зацикливаетесь. Правильно?

Альтернативный подход заключается в том, чтобы положить AtomicLongs в карту. Затем вы ставите/получаете AtomicLong и увеличиваете это.

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

В Java-вы можете быть в состоянии использовать 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(); 'без необходимости в другом' get', читать хэширование только один раз, а не дважды ... Даже без «AtomicLong» новый API позволяет оптимизировать вызов: 'histogram.merge (result, 1L, Long :: sum);' который уже является атомарным Обновить. – Holger

Смежные вопросы