First of all: I would recommend thinking one more time about using synchronized
, because:
- look at how simple such code is:
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;
}
- locks in java actually are pretty intelligent and have pretty good performance.
- you talk to DB in your code — the performance cost of that alone can be orders of magnitude higher than the performance cost of locks.
But in general, your race conditions happen because you update maxValue
and currentValue
independently.
You can combine these 2 values into a single immutable object and then work with the object atomically:
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;
}
}
After fixing that you will probably have to solve the following problems next:
- how to prevent multiple parallel
getFromDb();
(especially after taking into account that the method is idempotent)
- when one thread performs
getFromDb();
, how to prevent other threads from busy spinning inside while(true)
loop and consuming all available cpu time
- more similar problems
Solving each of these problems will probably make your code more and more complicated.
So, IMHO it is almost never worth it — locks work fine and keep the code simple.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…