java - 如何在不使用同步(无锁序列计数器实现)的情况下修复竞争条件?
问题描述
有一个场景,其中多个线程在比较代码上有竞争条件。
private int volatile maxValue;
private AtomicInteger currentValue;
public void constructor() {
this.current = new AtomicInteger(getNewValue());
}
public getNextValue() {
while(true) {
int latestValue = this.currentValue.get();
int nextValue = latestValue + 1;
if(latestValue == maxValue) {//Race condition 1
latestValue = getNewValue();
}
if(currentValue.compareAndSet(latestValue, nextValue) {//Race condition 2
return latestValue;
}
}
}
private int getNewValue() {
int newValue = getFromDb(); //not idempotent
maxValue = newValue + 10;
return newValue;
}
问题 :
解决此问题的明显方法是在 if 条件周围添加同步块/方法。在不使用任何类型的锁的情况下,使用并发 api 解决此问题的其他高性能方法是什么?
如何摆脱 while 循环,以便我们可以在没有或更少线程争用的情况下获得下一个值?
约束:
下一个 db 序列将按递增顺序排列,不一定均匀分布。因此它可能是 1、11、31,其中 21 可能已被其他节点询问。请求的下一个值将始终是唯一的。还需要确保所有序列都被使用,一旦我们达到先前范围的最大值,然后只向 db 请求另一个起始序列,依此类推。
例子 :
对于增量为 10 的 db next 序列 1、11、31,对于 30 个请求,输出的下一个序列应该是 1-10、11-20、31-40。
解决方案
首先:我建议再考虑一下使用synchronized
,因为:
- 看看这样的代码有多简单:
private int maxValue; private int currentValue; public constructor() { requestNextValue(); } public synchronized int getNextValue() { currentValue += 1; if (currentValue == maxValue) { requestNextValue(); } return currentValue; } private void requestNextValue() { currentValue = getFromDb(); //not idempotent maxValue = currentValue + 10; }
- Java中的锁实际上非常智能并且具有相当好的性能。
- 您在代码中与 DB 对话——仅此一项的性能成本就可能比锁的性能成本高几个数量级。
但总的来说,您的竞争条件发生是因为您maxValue
独立更新currentValue
。
您可以将这两个值组合成一个不可变对象,然后以原子方式使用该对象:
private final AtomicReference<State> stateHolder = new AtomicReference<>(newStateFromDb());
public int getNextValue() {
while (true) {
State oldState = stateHolder.get();
State newState = (oldState.currentValue == oldState.maxValue)
? newStateFromDb()
: new State(oldState.currentValue + 1, oldState.maxValue);
if (stateHolder.compareAndSet(oldState, newState)) {
return newState.currentValue;
}
}
}
private static State newStateFromDb() {
int newValue = getFromDb(); // not idempotent
return new State(newValue, newValue + 10);
}
private static class State {
final int currentValue;
final int maxValue;
State(int currentValue, int maxValue) {
this.currentValue = currentValue;
this.maxValue = maxValue;
}
}
修复该问题后,您接下来可能必须解决以下问题:
- 如何防止多个并行
getFromDb();
(特别是考虑到方法是幂等的之后) - 当一个线程执行时
getFromDb();
,如何防止其他线程忙于在while(true)
循环内旋转并消耗所有可用的 cpu 时间 - 更多类似问题
解决这些问题中的每一个都可能会使您的代码越来越复杂。
所以,恕我直言,这几乎是不值得的——锁可以正常工作并保持代码简单。
推荐阅读
- python - 如何使用 python/selenium/BeautifulSoup 在页面加载时抓取未完全加载的图像?
- unit-testing - 为什么我要在测试代码中使用 assert_not_raised?
- html - 如何让我的元素内联在左上角?
- java - jhipster 在生成应用程序时给出自动加载器错误
- javascript - Vite Vue-3 - 项目@不适用于 srcset
- flutter - 使用 video_cast 插件运行 pod install 时出错?
- python - 在给定时间段后删除标签
- dataframe - 来自 scikit-learn 的 RobustScaler 行为不正常
- javascript - 在 JavaScript 中创建动态对象
- apache-kafka - docker-compose 中的 Kafka:主题不在代理的首选副本中