java - 定时器类中潜在的竞争条件?

标签 java multithreading timer thread-safety

我编写了一个计时器,可以测量任何多线程应用程序中特定代码的性能。在下面的计时器中,它还会在 map 中填充花费了 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 毫秒之类的信息填充我的 map ,以便我稍后可以使用该 map 进行进一步的操作分析,例如计算平均值、中位数、第 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); 之间, map 中的值可能已更改。因此,oldValue 可能已过时?

最佳答案

您指出的部分看起来是正确的。是的,有时 oldValue 会过时,但这就是你循环的原因。对吗?

另一种方法是将 AtomicLongs 放入映射中。然后你放入/获取 AtomicLong 并递增它。

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

在 java 8 中,您可以使用 compute 和 friend 来发挥您的优势(测试并看看您最喜欢哪个):

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;
});

关于java - 定时器类中潜在的竞争条件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30904258/

相关文章:

java - 在同一个线程中创建多个计时器会导致主线程滞后

javascript - 使用javascript在php中计时器达到零时不调用提交方法

java - 在 GUI 中显示时间

java - 使用ajax和spring mvc上传多个文件

java - 抽象类上参数化方法的奇怪行为

c# - 如何线程/哪个线程用于后台 DLing .NET

Android fragment 和线程

Java:前n个整数的数组

java - 在 EditText (Android) 中过滤多个空格

c++ - 关于多线程程序中的临时对象