计时器类中潜在的竞争条件?

时间:2015-06-17 23:41:40

标签: java multithreading timer thread-safety

我编写了一个计时器,用于衡量任何多线程应用程序中特定代码的性能。在下面的计时器中,它还将使用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可能是陈旧的?

1 个答案:

答案 0 :(得分: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;
});