Следует ли использовать volatile для атрибутов классов модели домена в веб-приложениях Java? - PullRequest
1 голос
/ 16 марта 2011

Вот мое мышление:

Несмотря на то, что цикл HTTP-запроса по существу обрабатывается «одним потоком», каждый раз, когда HTTP-запрос обрабатывается для этого же сеанса, он может обрабатываться другим потоком из пула потоков.

Без использования ключевого слова volatile в объекте модели домена, жизненный цикл которого распространяется на несколько HTTP-запросов для одного и того же сеанса, тогда, насколько я понимаю, невозможно, чтобы атрибут мог быть локально кэшированным в потоке (оптимизация компилятором) в потоке, который обслуживал первый HTTP-запрос? Если второй HTTP-запрос обслуживается другим потоком, этот второй поток может не увидеть изменения в этом атрибуте, которые были сделаны первым потоком.

Означает ли это заклинание "Опасность Уилл Робинсон"? Или мне не хватает жизненно важного сюжета об использовании (или нет) ключевого слова volatile?

Ответы [ 6 ]

2 голосов
/ 16 марта 2011

Я думаю, вы забыли, что потоки, обрабатывающие HTTP-запрос, сначала должны извлечь экземпляр объекта модели домена из HttpSession, предоставленного вашим сервером приложений. Запрос обработки потока 2 в описываемом вами сценарии еще не имеет экземпляра этой доменной модели - он должен извлечь его из реализации сеанса в начале обработки каждого запроса.

Я думаю, что вполне разумно предположить, что реализация обработки сеансов на вашем сервере приложений обрабатывает данные сеансов таким образом, чтобы избежать проблем с видимостью модели памяти. Например, стандартная (не кластеризованная) реализация Apache Tomcat HttpSession хранит атрибуты сеанса в ConcurrentHashMap .

Добавление volatile мне кажется совершенно ненужным. Я никогда не видел, чтобы это было сделано для объектов модели домена, обрабатываемых HTTP-запросами в среде сервлетов, ни в одном проекте, в котором я работал.

Это была бы другая история, если бы thread-1 и thread-2 имели ссылки на один и тот же экземпляр объекта одновременно при обработке двух разных запросов, и вас беспокоило, что изменения в одном потоке будут видны другие , поскольку каждый обрабатывает запрос, но это не похоже на то, о чем вы спрашиваете.

1 голос
/ 16 марта 2011

Да, если вы разделяете объект между различными потоками, у вас могут быть условия гонки. Без отношения «случается до» записи, сделанные одним потоком, могут не просматриваться чтением в другом потоке.

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

Это сложная проблема, просто использование изменчивого ключевого слова, вероятно, не является хорошим решением.

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

Оказывается, что volatile не нужна была в конце концов.Проблема, которая «казалась» исправленной с помощью volatile, была на самом деле очень тонкой ошибкой, чувствительной ко времени, которая была исправлена ​​гораздо более элегантно и правильно;)

Так что sbrigdes был прав, когда сказал «просто используя volatileКлючевое слово, вероятно, не является хорошим решением. "

0 голосов
/ 16 марта 2011

Если у вас есть проблемы с параллелизмом, простое добавление 'volatile', вероятно, вам не поможет.

Что касается сохранения объекта в качестве атрибута Session, я бы порекомендовал вам оставить только идентификатор объекта,и использовать его для извлечения «живого» экземпляра, когда он вам нужен (если вы используете Hibernate, последовательные извлечения возвращают один и тот же объект, поэтому это не должно вызывать проблем с производительностью).Инкапсулируйте всю логику модификации для этого конкретного объекта в единый фасад и выполняйте управляющий параллелизм там, используя блокировку dababase.

Или, если вы действительно, действительно хотите использовать блокировку на основе памяти, и действительноубедитесь, что у вас никогда не будет двух экземпляров приложения, запущенного в кластере, убедитесь, что ваша логика фасада синхронизирована на нужном уровне.Если ваша синхронизация слишком мелкая (низкоуровневые операции, , например, переменные переменные ), вероятно, этого будет недостаточно, чтобы сделать ваш код поточно-ориентированным.Например, java.util.Hashtable полностью синхронизирован, но это ничего не значит, если у вас есть такая логика:

01    if (!hashtable.containsKey(key)) {
02        hashtable.put(key, calculate(key));
03    }

Если два потока, скажем, t1 и t2, попадут в этот блокв то же время t1 может выполнить строку 01, затем t2 также может выполнить 01, а затем 02, и t1 затем выполнит 02, переписав то, что сделал t2,Операции containsKey() и put() являются атомарными по отдельности, но то, что должно быть атомарным, это весь блок.

Иногда пересчет значения не имеет значения, но иногда это имеет значение, и будет перерыв.

Когда дело доходит до параллелизма, волшебства нет.Я имею в виду, шов некоторые дурацкие фреймворки пытаются продать вам идею, что они решают эту проблему для вас.Они неДаже если он работает в 99% случаев, он впечатляюще сломается, когда вы начнете работать и начнете получать интенсивный трафик.Или (намного, намного) хуже, он будет молча генерировать неправильные результаты.

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

0 голосов
/ 16 марта 2011

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

Скажем, у вас есть пользовательский объект в сеансе.Большинство запросов просто извлекают его, читают и отображают.

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

В этом случае поля в User не нуждаются в защите;безопасность потока гарантируется сессией setAttribute () - getAttribute ()

0 голосов
/ 16 марта 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...