Will the use of a final parameter preclude a class from being thread safe?
My tests indicate that the following code is not thread safe even though the class is stateless and all state shared between methods is passed as parameters from method to method. A single instance of the following class is being invoked by multiple threads.
public class ThingFinder {
Toke开发者_如何学Pythonn findFoo(TokenIterator<? extends Token> iterator,
Token start, final Token limit) {
Token partOfFoo = searchForward(iterator, start, new TokenSearcher() {
int maxTokens = 5;
@Override
public SearchAction assessToken(Token aToken) {
if (limit != null && (aToken.getStart() >= limit.getStart())) {
return SearchAction.STOP;
}
if (maxTokens-- == 0) {
return SearchAction.STOP;
}
if (isAThing(aToken)) {
return SearchAction.MATCH;
} else {
return SearchAction.IGNORE;
}
}
});
return partOfFoo;
}
public Token extractAThing(TokenIterator<? extends Token> iterator) {
Token start = findStart(iterator);
Token limit = findLimit(iterator, start);
return findFoo(iterator, start, limit);
}
}
The intent was for this class to be thread safe due to the fact that is is stateless and all state that needs to be shared among methods is passed from method to method as parameters. However, the tests indicate that sometimes we get a null pointer exception at this line:
if (limit != null && (aToken.getStart() >= limit.getStart())) {
It seems that sometime between the null value check and the invocation of getStart the parameter limit is becoming null.
Note that the method findFoo declares the limit parameter to be final:
Token findFoo(TokenIterator<? extends Token> iterator, Token start,
final Token limit) {
Is it the case that final method parameters are not on the stack frame but instead one instance is shared among all invocations of the method? If it is true that there is one instance shared among all invocations then does this imply that using final parameters makes a class inherently thread Unsafe?
Is it the case that final method parameters are not on the stack frame but instead one instance is shared among all invocations of the method? If it is true that there is one instance shared among all invocations then does this imply that using final parameters makes a class inherently thread Unsafe?
No, I think you are mixing something here.
What the final
modifier here does is allowing the local variable limit
to be copied into a synthetic variable of the anonymous TokenSearcher
instance. This copying occurs during the construction of this instance, and then it will be used by the assessToken
method. This synthetic variable will still be final (or at least not modified), thus there should be no problem here if your framework does not do reflection magic (and this in parallel, still).
Still each call of findFoo
will have its own parameters.
As said by Reed, the unboxing of the getStart result is a more probable culprit.
The same exception may also be produced if getStart() is returning a Long or Integer type, but the value is null. The exception is caused by un-boxing to integer or long for the >= comparison, but the native types cannot be null.
It seems that sometime between the null value check and the invocation of getStart the parameter limit is becoming null.
That is not possible. At the point you are testing and using limit
it is (effectively) a private final instance variable of a thread-confined object. The fact that it is final means that it won't change. If it is non-null to start with, it will stay that way. The fact that the object is thread confined means that no other thread apart from the current one can get to it anyway.
(The only potential thread safety issue I can think of might arise if your searchForward
method passed the TokenSearcher
object off to a different thread; i.e. the object was not thread-confined. Might that be happening?)
I think that the real problem is something else:
- The
aStart
parameter could benull
. - If one or other of the
getStart()
methods is declared to return a boxed type, they might be returningnull
.
Either of these could result in an NPE in that line.
Can the anonymous inner class live past the method invocation?
It could do. It entirely depends on what the searchForward
method call does.
If so what Limit variable will it access?
The code of the anonymous inner class accesses a copy of limit
held as an instance variable of the TokenSearcher
object. It is initialized when the object is created, and is final
.
In regards to your question in general, each method invocation does get its own instance of the final parameter. Final does not create a static variable. It simply allows the variable to be set only once.
精彩评论