Is LockFreeBucket tryConsumeImpl Thread Safe? #157
-
protected boolean tryConsumeImpl(long tokensToConsume) {
StateWithConfiguration previousState = stateRef.get(); // <- At Time T2: thread B reads the reference to StateWithConfiguration object
StateWithConfiguration newState = previousState.copy(); // <- At Time T3: thread B reads members of StateWithConfiguration object X
long currentTimeNanos = timeMeter.currentTimeNanos();
while (true) {
newState.refillAllBandwidth(currentTimeNanos);
long availableToConsume = newState.getAvailableTokens();
if (tokensToConsume > availableToConsume) {
return false;
}
newState.consume(tokensToConsume);
if (stateRef.compareAndSet(previousState, newState)) { // <- At Time T1: thread A publishes StateWithConfiguration object X
return true;
} else {
previousState = stateRef.get();
newState.copyStateFrom(previousState); // <- At Time T0: thread A writes to members of StateWithConfiguration object X
}
}
} However, members of StateWithConfiguration are not marked as According to Java Language Specs:
So, I think the writes at time T0 may not be visible to the reads at time T2, and thus this method is not thread safe. Am I right? Or I just misunderstood the java specification.. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
@zhangchengkai826 hello, JMM is the hardest part of java specification. Typically regular developer need to read it 10-20 times in combination with reading non-formal JMM explanations like this Java Memory Model Pragmatics and others. Let's check the statement that reading in action T3 does not see the write that was made by action T0.
It worse to mention the following facts about the actions above:
To confirm our statement that it is possible to have a valid execution where T0 is not related with happens-before with T3 we need to provide a possible example of execution which is not contradictory with happens-before rules. Also, it needs to keep in mind that following happens-before relations between actions above:
First attemp:
In the table above T0 is not hb to T3, but execution above is not valid because as was mentioned above T0 related with hb to T1. Second attemp:
In the table above T0 is not hb to T3, but execution above is not valid because as was mentioned above T1 related with hb to T2. Third attemp:
In the table above T0 is not hb to T3, but execution above is not valid because as was mentioned above T2 related with hb to T3. ... So, you can continue this exercise but all attempts to build an order where there is not hb(T0, T3) will be failed.
Because of the transient property of happens-before order and hb(T0, T1) and hb(T1, T2) we have that hb(T0, T2). Also because hb(T2, T3) we have that hb(T0, T3). So our code is correct because we found one execution where hb(T0, T3) and failed to find any execution where not hb(T0, T3). P.S.
According to your original question this rule says that if thread B sees T1 it also sees T0 as well. |
Beta Was this translation helpful? Give feedback.
@zhangchengkai826 hello,
JMM is the hardest part of java specification. Typically regular developer need to read it 10-20 times in combination with reading non-formal JMM explanations like this Java Memory Model Pragmatics and others.
Let's check the statement that reading in action T3 does not see the write that was made by action T0.
We have two thread A and B and four actions:
It worse to mention the following facts about the acti…