Мои тесты показывают, что следующий код не является потокобезопасным, даже если класс не имеет состояния и все общие состояния между методами передаются как параметры от метода к методу.Один экземпляр следующего класса вызывается несколькими потоками.
public class ThingFinder {
Token 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);
}
}
Предполагалось, что этот класс будет потокобезопасным, поскольку он не имеет состояния и все состояния, которые должны быть разделены между методами, передаются от метода к методу в качестве параметров.Однако тесты показывают, что иногда мы получаем исключение нулевого указателя в этой строке:
if (limit != null && (aToken.getStart() >= limit.getStart())) {
Кажется, что когда-то между проверкой нулевого значения и вызовом getStart предел параметра становится нулевым.
Обратите внимание, что метод findFoo объявляет параметр limit как final:
Token findFoo(TokenIterator<? extends Token> iterator, Token start,
final Token limit) {
Это тот случай, когда конечные параметры метода не находятся в кадре стека, а вместо этого один экземпляр является общим для всех вызововметод?Если это правда, что существует один экземпляр, общий для всех вызовов, значит ли это, что использование конечных параметров делает класс по своей сути потоком небезопасным?