Будет ли использование окончательного параметра препятствовать тому, чтобы класс был потокобезопасным? - PullRequest
4 голосов
/ 27 мая 2011

Мои тесты показывают, что следующий код не является потокобезопасным, даже если класс не имеет состояния и все общие состояния между методами передаются как параметры от метода к методу.Один экземпляр следующего класса вызывается несколькими потоками.

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) {

Это тот случай, когда конечные параметры метода не находятся в кадре стека, а вместо этого один экземпляр является общим для всех вызововметод?Если это правда, что существует один экземпляр, общий для всех вызовов, значит ли это, что использование конечных параметров делает класс по своей сути потоком небезопасным?

Ответы [ 4 ]

4 голосов
/ 27 мая 2011

Это тот случай, когда конечные параметры метода не находятся в кадре стека, а вместо этого один экземпляр является общим для всех вызовов метода?Если это правда, что существует один экземпляр, общий для всех вызовов, то означает ли это, что использование конечных параметров делает класс по сути потоком небезопасным?

Нет, я думаю, что вы что-то тут смешиваете.

Модификатор final позволяет копировать локальную переменную limit в синтетическую переменную анонимного экземпляра TokenSearcher.Это копирование происходит во время создания этого экземпляра, и затем оно будет использоваться методом assessToken.Эта синтетическая переменная все еще будет конечной (или, по крайней мере, не модифицированной), поэтому здесь не должно быть проблем, если ваша структура не выполняет магию отражения (и это параллельно, все же).

Тем не менее каждый вызов findFoo будет иметь свои собственные параметры.

Как сказал Рид, распаковка результата getStart является более вероятным виновником.

1 голос
/ 27 мая 2011

Кажется, что иногда между проверкой нулевого значения и вызовом getStart предел параметра становится нулевым.

Это невозможно.В момент, когда вы тестируете и используете limit, это (эффективно) частная конечная переменная экземпляра объекта, ограниченного потоком.Тот факт, что он окончательный, означает, что он не изменится.Если это не нулевое значение для начала, оно останется таким.Тот факт, что объект ограничен потоком, означает, что никакой другой поток, кроме текущего, все равно не может получить к нему доступ.

(Единственная потенциальная проблема безопасности потока, о которой я могу подумать , может возникнуть , еслиВаш searchForward метод передал объект TokenSearcher другому потоку, т. е. объект был не ограниченным потоком. Может ли это происходить?)

Я думаю, что настоящая проблемачто-то еще:

  • Параметр aStart может быть null.
  • Если один или другой из методов getStart() объявлен для возврата в штучной упаковке, они могутвозвращать null.

Любой из них может привести к NPE в этой строке.


Может ли анонимный внутренний класс жить после вызова метода?

Это может сделать.Это полностью зависит от того, что делает вызов метода searchForward.

Если да, то к какой переменной Limit он получит доступ?

Код анонимного внутреннего класса обращается к копииlimit хранится как переменная экземпляра объекта TokenSearcher.Он инициализируется при создании объекта и имеет значение final.

1 голос
/ 27 мая 2011

Такое же исключение также может возникнуть, если getStart () возвращает тип Long или Integer, но значение равно нулю.Исключение вызвано распаковкой целого или длинного для сравнения> =, но собственные типы не могут быть нулевыми.

0 голосов
/ 27 мая 2011

Что касается вашего вопроса в целом, каждый вызов метода получает свой экземпляр конечного параметра. Final не создает статическую переменную. Он просто позволяет установить переменную только один раз.

...