Нужно ли объявлять переменную, к которой обращаются несколько потоков в сервлете Java, volatile?

В книге Программирование сервлетов Java есть пример сервлет на странице 54, который ищет простые числа в фоновом потоке. Каждый раз, когда клиент обращается к сервлету, возвращается последнее найденное простое число.

Переменная, которая используется для хранения самого последнего найденного простого числа, объявляется как таковая:

long lastprime = 0;

Поскольку доступ к этой переменной начинается из нескольких потоков (фонового потока, выполняющего вычисления, и любых клиентских потоков, обращающихся к ней), не нужно ли объявлять ее изменчивой или каким-то образом синхронизировать ее доступ?


person MCS    schedule 29.07.2009    source источник


Ответы (5)


Да, если вы действительно хотите увидеть самое последнее вычисленное простое число в любом потоке, оно должно быть либо изменчивым, либо доступ к нему потокобезопасным способом с помощью synchronized блоков/методов. Кроме того, как указано в комментариях, энергонезависимые длинные переменные не могут быть обновлены атомарно, поэтому вы можете видеть верхние 32 бита старого значения и нижние 32 бита нового значения (или наоборот).

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

Это не SingleThreadModel сервлет, не так ли? Это, очевидно, имело бы значение.

Другой альтернативой было бы использование AtomicLong .

person Jon Skeet    schedule 29.07.2009
comment
Нет, это не сервлет SingleThreadModel. Это пример использования сервлета для фоновой обработки. - person MCS; 29.07.2009
comment
Тогда это просто плохой пример. Бывает - авторы никогда не знают, о чем говорят ;) - person Jon Skeet; 29.07.2009
comment
Это не просто просмотр самого последнего значения, это (а) просмотр ЛЮБОГО опубликованного значения — нет никакой гарантии, что вы не будете постоянно видеть «0» в потоке чтения, и, что более важно (б) вообще увидеть правильное значение. Обновления энергонезависимых длин не являются атомарными, вы можете увидеть нижние 32 байта старого значения и верхние 32 байта нового (или наоборот) — что может вообще не быть простым числом! - person Cowan; 30.07.2009
comment
Правда, я пренебрегал атомарной стороной вещей. Обновление... (Меня не так беспокоит любой опубликованный бит значения, так как это похоже на просто обобщение самого последнего значения.) - person Jon Skeet; 30.07.2009

да. Переменные сервлета не являются потокобезопасными.

person Zack Marrapese    schedule 29.07.2009

Существует чистое разделение чтения/записи между потоками; один поток «публикует» последнее простое число для чтения другими, тогда вам может сойти с рук сделать его изменчивым.

Если шаблон доступа включает некоторые последовательности чтения-изменения-записи и т.п., вам придется синхронизировать доступ к полю.

person Chris Vest    schedule 29.07.2009

Предполагая, что Java 5 или более поздняя версия, а затем объявление ее изменчивой, дает четко определенную семантику, как описано здесь. Исходя из принципа устранения сомнений у сопровождающего кода, я бы использовал volatile, сказав: «Да, я знаю, что несколько потоков используют эту переменную».

Интересный вопрос заключается в том, что эффект не объявляется изменчивым. Если у вас есть первое прайм, имеет ли значение, что это самое последнее из доступных? Volatile гарантирует, что значения берутся из памяти, а не из кэшей «ЦП», поэтому вы должны получить более актуальное значение.

А как насчет возможности увидеть частичное присвоение? Могли бы вам действительно не повезти и увидеть лонг, чьи младшие биты являются частью старого значения, а старшие биты частью другого значения? Что ж, присваивания значениям long и double не являются атомарными, так что теоретически да!

Следовательно, volatile или синхронизированный - это не просто приятно иметь ... это вам нужно

person djna    schedule 29.07.2009

Семантика volatile переменной в Java недостаточно сильна, чтобы сделать операцию приращения (lastprime++) атомарной, если вы не можете гарантировать, что переменная записывается только из одного потока - не в случае сервлета

С другой стороны, использование переменных AtomicXXX является потокобезопасным, если не выполняются сложные операции. Будет окно уязвимости при обновлении более чем одной атомарной переменной, даже если каждый вызов является атомарным.

person Ovidiu Lupas    schedule 30.07.2009