线程安全的计算移动平均线的方法

时间:2016-05-06 15:31:18

标签: java multithreading thread-safety moving-average

我有一个库,可以对我的服务进行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);
    }
}

此代码线程是否安全,因为这是从多线程程序调用的?如果不是,我怎么能做这个多线程。

我想确保此运行平均计算速度很快,因为此库在非常繁重的负载下运行,因此这不会增加整体延迟。另外我怀疑我甚至需要在这里使用BigDecimaldoublelong可能会在这里使用。

2 个答案:

答案 0 :(得分: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,但会降低执行速度

答案 1 :(得分:0)

为什么不使用 http://mvnrepository.com/artifact/com.codahale.metrics? 然后创建度量标准注册表,并使用例如直方图。它会给你平均值和更多(p99,p999)

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

然后在代码中:

histogram.update(operationTimeInMilliseconds)