我需要为Web服务器实现全局对象收集统计信息。我有Statistics
单身人士,其方法为addSample(long sample)
,随后会调用updateMax
。这显然是线程安全的。我有这种方法来更新整个统计信息的最大值:
AtomicLong max;
private void updateMax(long sample) {
while (true) {
long curMax = max.get();
if (curMax < sample) {
boolean result = max.compareAndSet(curMax, sample);
if (result) break;
} else {
break;
}
}
}
此实施是否正确?我正在使用java.util.concurrent,因为我相信它会比简单的synchronized
更快。是否有其他/更好的方法来实现这一点?
答案 0 :(得分:11)
我认为这是正确的,但为了清晰起见,我可能会重写一点,肯定会添加评论:
private void updateMax(long sample) {
while (true) {
long curMax = max.get();
if (curMax >= sample) {
// Current max is higher, so whatever other threads are
// doing, our current sample can't change max.
break;
}
// Try updating the max value, but only if it's equal to the
// one we've just seen. We don't want to overwrite a potentially
// higher value which has been set since our "get" call.
boolean setSuccessful = max.compareAndSet(curMax, sample);
if (setSuccessful) {
// We managed to update the max value; no other threads
// got in there first. We're definitely done.
break;
}
// Another thread updated the max value between our get and
// compareAndSet calls. Our sample can still be higher than the
// new value though - go round and try again.
}
}
编辑:通常我至少首先尝试同步版本,只有在我发现它导致问题时才会使用这种无锁代码。
答案 1 :(得分:9)
自Java 8起,引入了LongAccumulator。 建议为
当多个线程时,此类通常优于AtomicLong 更新用于收集等目的的公共值 统计,而不是细粒度的同步控制。低下 更新争用,这两个类具有相似的特征。但 在高争用的情况下,这个类的预期吞吐量是 以更高的空间消耗为代价,显着提高。
您可以按如下方式使用它:
LongAccumulator maxId = new LongAccumulator(Long::max, 0); //replace 0 with desired initial value
maxId.accumulate(newValue); //from each thread
答案 2 :(得分:2)
我相信你所做的是正确的,但我认为这是一个更简单的版本。
private void updateMax(long sample){
//this takes care of the case where between the comparison and update steps, another thread updates the max
//For example:
//if the max value is set to a higher max value than the current value in between the comparison and update step
//sample will be the higher value from the other thread
//this means that the sample will now be higher than the current highest (as we just set it to the value passed into this function)
//on the next iteration of the while loop, we will update max to match the true max value
//we will then fail the while loop check, and be done with trying to update.
while(sample > max.get()){
sample = max.getAndSet(sample);
}
}
答案 3 :(得分:2)
好像你没有选择答案,这是我的:
// while the update appears bigger than the atomic, try to update the atomic.
private void max(AtomicDouble atomicDouble, double update) {
double expect = atomicDouble.get();
while (update > expect) {
atomicDouble.weakCompareAndSet(expect, update);
expect = atomicDouble.get();
}
}
它与接受的答案大致相同,但不使用我个人不喜欢的break
或while(true)
。
编辑:刚刚在java 8中发现了DoubleAccumulator
。文档甚至说这是针对像你这样的摘要统计问题:
DoubleAccumulator max = new DoubleAccumulator(Double::max, Double.NEGATIVE_INFINITY);
parallelStream.forEach(max::accumulate);
max.get();
答案 4 :(得分:2)
使用Java 8,您可以利用功能接口和简单的lamda表达式来解决这个问题,只需一行而不是循环:
private void updateMax(long sample) {
max.updateAndGet(curMax -> (sample > curMax) ? sample : curMax);
}
该解决方案使用updateAndGet(LongUnaryOperator)
方法。当前值包含在curMax
中,并且如果样本值大于当前最大值,则使用条件运算符执行简单测试,将当前最大值替换为样本值。