计时器 class 中的潜在竞争条件?
Potential race condition in the timer class?
我编写了一个计时器,它可以测量任何多线程应用程序中特定代码的性能。在下面的计时器中,它还会用 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;
}
}
例如,这是我将使用上面的计时器 class 来测量多线程应用程序中特定代码的性能的方法:
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
可能会过时?
你说的部分看起来是正确的。是的,有时 oldValue 会过时,但这就是你循环的原因。对吗?
另一种方法是将 AtomicLongs 放入映射中。然后你 put/get 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;
});
我编写了一个计时器,它可以测量任何多线程应用程序中特定代码的性能。在下面的计时器中,它还会用 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;
}
}
例如,这是我将使用上面的计时器 class 来测量多线程应用程序中特定代码的性能的方法:
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
可能会过时?
你说的部分看起来是正确的。是的,有时 oldValue 会过时,但这就是你循环的原因。对吗?
另一种方法是将 AtomicLongs 放入映射中。然后你 put/get 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;
});