max 的线程安全实现

发布于 2024-11-08 12:04:28 字数 619 浏览 0 评论 0原文

我需要为网络服务器实现全局对象收集统计信息。我有 Statistics 单例,它有方法 addSample(long example),随后调用 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 更快。还有其他/更好的方法来实现这个吗?

I need to implement global object collecting statistics for web server. I have Statistics singleton, which has method addSample(long sample), which subsequently call updateMax. This has to be obviously thread-safe. I have this method for updating maximum of whole Statistics:

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

Is this implementation correct? I am using java.util.concurrent, because I believe it would be faster than simple synchronized. Is there some other / better way to implement this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

遮了一弯 2024-11-15 12:04:28

从 Java 8 开始,LongAccumulator 已介绍。
建议作为

当多线程时,此类通常优于 AtomicLong
更新用于收集等目的的通用值
统计,不用于细粒度的同步控制。低位以下
更新争用,这两个类具有相似的特征。但
在高竞争情况下,此类的预期吞吐量为
明显更高,但代价是空间消耗更大。

您可以按如下方式使用它:

LongAccumulator maxId = new LongAccumulator(Long::max, 0); //replace 0 with desired initial value
maxId.accumulate(newValue); //from each thread

As of Java 8, LongAccumulator has been introduced.
It is advised as

This class is usually preferable to AtomicLong when multiple threads
update a common value that is used for purposes such as collecting
statistics, not for fine-grained synchronization control. Under low
update contention, the two classes have similar characteristics. But
under high contention, expected throughput of this class is
significantly higher, at the expense of higher space consumption.

You can use it as follows:

LongAccumulator maxId = new LongAccumulator(Long::max, 0); //replace 0 with desired initial value
maxId.accumulate(newValue); //from each thread
§普罗旺斯的薰衣草 2024-11-15 12:04:28

我认为这是正确的,但为了清楚起见,我可能会稍微重写一下,并且肯定添加注释:

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.
    }
}

编辑:通常我至少首先尝试同步版本,并且只有当我发现这种无锁代码引起问题时才使用它。

I think it's correct, but I'd probably rewrite it a little for clarity, and definitely add comments:

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.
    }
}

EDIT: Usually I'd at least try the synchronized version first, and only go for this sort of lock-free code when I'd found that it was causing a problem.

淡淡绿茶香 2024-11-15 12:04:28

使用 Java 8,您可以利用函数式接口和简单的 lamda 表达式,只需一行代码即可解决此问题,无需循环:

private void updateMax(long sample) {
    max.updateAndGet(curMax -> (sample > curMax) ? sample : curMax);
}

该解决方案使用 updateAndGet(LongUnaryOperator) 方法。当前值包含在 curMax 中,如果样本值大于当前最大值,则使用条件运算符执行简单测试,用样本值替换当前最大值。

With Java 8 you can take advantage of functional interfaces and a simple lamda expression to solve this with one line and no looping:

private void updateMax(long sample) {
    max.updateAndGet(curMax -> (sample > curMax) ? sample : curMax);
}

The solution uses the updateAndGet(LongUnaryOperator) method. The current value is contained in curMax and using the conditional operator a simple test is performed replacing the current max value with the sample value if the sample value is greater than the current max value.

等风也等你 2024-11-15 12:04:28

就好像您没有选择答案一样,这是我的:

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

它与接受的答案或多或少相同,但不使用 breakwhile(true) 我个人不喜欢这一点。

编辑:刚刚在java 8中发现了DoubleAccumulator。文档甚至说这是针对像您这样的汇总统计问题:

DoubleAccumulator max = new DoubleAccumulator(Double::max, Double.NEGATIVE_INFINITY);
parallelStream.forEach(max::accumulate);
max.get();

as if you didn't have your pick of answers, here's mine:

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

it's more or less the same as the accepted answer, but doesn't use break or while(true) which I personally don't like.

EDIT: just discovered DoubleAccumulator in java 8. the documentation even says this is for summary statistics problems like yours:

DoubleAccumulator max = new DoubleAccumulator(Double::max, Double.NEGATIVE_INFINITY);
parallelStream.forEach(max::accumulate);
max.get();
空宴 2024-11-15 12:04:28

我相信你所做的是正确的,但这是一个更简单的版本,我也认为是正确的。

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

I believe what you did is correct, but this is a simpler version that I also think is correct.

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);  
      }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文