2016-05-06 5 views
2

У меня есть библиотека, которая вызывает HTTP-запросы к моей службе. Я пытался подсчитать среднее значение времени, в течение которого мой сервис получает среднее значение.поток безопасный способ вычисления скользящего среднего

Вот основная логика того, как я рассчитываю «средний уровень».

import java.math.BigDecimal; 
import java.math.RoundingMode; 
import java.util.LinkedList; 
import java.util.Queue; 

public class MovingAverage { 

    private final Queue<BigDecimal> window = new ArrayDeque<BigDecimal>(); 
    private final int period; 
    private BigDecimal sum = BigDecimal.ZERO; 

    public MovingAverage(int period) { 
     this.period = period; 
    } 

    public void add(BigDecimal num) { 
     sum = sum.add(num); 
     window.add(num); 
     if (window.size() > period) { 
      sum = sum.subtract(window.remove()); 
     } 
    } 

    public BigDecimal getAverage() { 
     if (window.isEmpty()) return BigDecimal.ZERO; 
     BigDecimal divisor = BigDecimal.valueOf(window.size()); 
     return sum.divide(divisor, 2, RoundingMode.HALF_UP); 
    } 
} 

Этот код безопасен, потому что он вызывается из многопоточной программы? Если нет, то как я могу сделать это многопоточным.

Я хочу убедиться, что этот средний расчет выполняется быстро, поскольку эта библиотека работает под очень большой нагрузкой, поэтому это не должно увеличивать общую задержку. Также я сомневаюсь, что мне даже нужно использовать BigDecimal здесь, double или long может работать здесь.

+0

Из ArrayDeque API: «Они не поточно-, при отсутствии внешней синхронизации , они не поддерживают одновременный доступ несколькими потоками ». Я не вижу, чтобы вы внешне синхронизировали что-нибудь, поэтому ... – azurefrog

+0

Вам нужно использовать BigDecimal? Похоже, вы создаете и уничтожаете три объекта BigDecimal для каждого вызова 'movingAverage.add (...)'. Если бы вы могли использовать 'long' и' long [] 'array или' double' и 'double []' array, то это было бы намного быстрее. –

+0

@jameslarge Можете ли вы привести пример, как это будет выглядеть с 'long' или' double'? – john

ответ

1

этот код не потокобезопасными, представьте себе следующую последовательность:

initial state: MovingAverage queue is empty 
thread 1: calls add(1), sum is 1, window size is 1 
thread 2: calls add(1), pauses after sum = sum.add(num), sum is 2, window size is 1 
thread 1: calls getAverage, it will return 2/1 = 2 

другой случай использования:

thread 1: calls add(1), pauses after sum.add(num), but before sum = 
thread 2: calls add(1), sets sum to 1 
thread 1: continues, overwrites sum with 1, but should be 2, as windows size is updated to 2 

самый простой способ сделать это ступать безопасно - добавить synchronized перед каждым методом, но будет замедлять выполнение

+0

Замедлить его по сравнению с чем? –

+0

@SotiriosDelimanolis для несинхронизированной версии –

+0

Для несинхронизированной версии _incorrect_? –

0

Почему бы вам не использовать http://mvnrepository.com/artifact/com.codahale.metrics? Затем создайте реестр метрик и используйте, например, гистограмму. Он будет дает среднюю и более (P99, P999)

MetricRegistry metricRegistry = new MetricRegistry(); 
Histogram histogram = metricRegistry.histogram("stats"); 

, а затем в коде:

histogram.update(operationTimeInMilliseconds)