gpt4 book ai didi

java - 计算方法的并发调用

转载 作者:行者123 更新时间:2023-11-30 07:51:20 25 4
gpt4 key购买 nike

假设我有一个非常简单的网络服务,其唯一任务是计算它的端点被调用的次数。端点是 /hello

@Controller
public class HelloController {

private int calls = 0;

@RequestMapping("/hello")
public String hello() {
incrementCalls();
return "hello";
}

private void incrementCalls() {
calls++;
}
}

现在,只要两个用户不同时调用 /hello 就可以正常工作。但是当确实发生对 /hello 的并行调用时,calls 变量只会递增一次(如果我没记错的话)。所以很明显,这里需要进行某种同步。

问题是使该方法线程安全的最佳方法是什么?

最佳答案

calls++ 可能导致不同于您预期的行为的原因是它不是原子。原子操作以这样一种方式发生,即整个操作不能被另一个线程拦截。原子性是通过锁定操作或利用已经以原子方式执行操作的硬件来实现的。

递增很可能不是原子操作,因为它是 calls = calls + 1; 的快捷方式。确实可能发生两个线程在任何一个有机会递增之前为 calls 检索相同的值。然后两者将存储相同的值,而不是一个获得已经增加的值。

有几种简单的方法可以将 get-and-increment 转换为原子操作。对您来说最简单的方法,不需要任何导入,就是让您的方法同步:

private void incrementCalls() {
calls++;
}

每当线程进入该方法时,这将隐式锁定它所属的 HelloController 对象。其他线程将不得不等到锁被释放后才能进入该方法,使整个方法成为一个原子操作。

另一种方法是显式同步您想要的代码部分。对于不需要大量原子操作的大型方法来说,这通常是更好的选择,因为同步在时间和空间上相当昂贵:

private void incrementCalls() {
sychronized(this) {
calls++;
}
}

使整个方法synchronized只是将其全部内容包装在synchronized(this)中的快捷方式。

java.util.concurrent.atomic.AtomicInteger处理同步以使您希望对整数执行的大部分操作成为您的原子操作。在这种情况下,您可以调用 getAndAdd(1) , 或 getAndIncrement() .就保持代码的易读性而言,这可能是最干净的解决方案,因为它减少了大括号的数量并使用了精心设计的库函数。

关于java - 计算方法的并发调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47267452/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com