Основные проблемы, которые я видел, заключались в том, чтобы (а) избежать тупиков и (б) обмениваться данными между потоками. Беспокойство арендодателя (но только немного меньше) - избегать узких мест. Я уже сталкивался с несколькими проблемами с разрозненной блокировкой вне последовательности, вызывающей взаимные блокировки - очень хорошо сказать «всегда получать блокировки в одном и том же порядке», но в средних и больших системах практически невозможно обеспечить это.
Предостережение: когда я придумал это решение, мне пришлось ориентироваться на Java 1.1 (поэтому пакет для параллелизма еще не был мерцанием в глазах Дуга Ли) - имеющиеся инструменты были полностью синхронизированы и ждали / уведомляли. Я опирался на опыт написания сложной многопроцессорной системы связи с использованием системы сообщений в реальном времени QNX.
Основываясь на своем опыте работы с QNX, который имел проблему тупиковой ситуации, но избегал параллелизма данных, копируя сообщения из пространства памяти одного процесса в другое, я придумал основанный на сообщениях подход для объектов - который я назвал IOC, для интер -объект координации. На начальном этапе, который я предусмотрел, я мог бы создать все моих объектов подобным образом, но в ретроспективе выясняется, что они необходимы только в основных контрольных точках в большом приложении - «межгосударственных развязках», если вы будете не подходит для каждого «перекрестка» в дорожной системе. Это оказывается главным преимуществом, потому что они совершенно не POJO.
Я предполагал систему, в которой объекты не будут концептуально вызывать синхронизированные методы, а вместо этого будут "отправлять сообщения". Сообщения могут быть отправкой / ответом, когда отправитель ждет, пока сообщение обрабатывается и возвращается с ответом, или асинхронным, когда сообщение отбрасывается в очередь, снимается с очереди и обрабатывается на более позднем этапе. Обратите внимание, что это концептуальное различие - обмен сообщениями был реализован с использованием синхронизированных вызовов методов.
Основными объектами для системы обмена сообщениями являются IsolatedObject, IocBinding и IocTarget.
Изолированный объект называется так, потому что у него нет открытых методов; это то, что расширяется для получения и обработки сообщений. Используя отражение, далее принудительно утверждается, что дочерний объект не имеет ни открытых методов, ни пакетов или защищенных методов, кроме тех, которые унаследованы от IsolatedObject, почти все из которых являются окончательными; сначала это выглядит очень странно, потому что когда вы создаете подкласс IsolatedObject, вы создаете объект с 1 защищенным методом:
Object processIocMessage(Object msgsdr, int msgidn, Object msgdta)
и все остальные методы являются частными для обработки определенных сообщений.
IocTarget является средством абстрагирования видимости изолированного объекта и очень полезен для придания другому объекту собственной ссылки для отправки вам сигналов назад, без раскрытия фактической ссылки на объект.
И IocBinding просто привязывает объект отправителя к получателю сообщения, чтобы проверки проверки не выполнялись для каждого отправленного сообщения, а создавался с использованием IocTarget.
Все взаимодействие с изолированными объектами происходит через «отправку» им сообщений - метод processIocMessage получателя синхронизируется, что обеспечивает одновременную обработку только одного сообщения.
Object iocMessage(int mid, Object dta)
void iocSignal (int mid, Object dta)
Создав ситуацию, когда вся работа, выполняемая изолированным объектом, направляется одним способом, я затем расположил объекты в объявленной иерархии посредством «классификации», которую они объявляют при построении - просто строка, которая идентифицирует их как будучи одним из любого числа «типов получателей сообщений», который помещает объект в некоторую заранее определенную иерархию. Затем я использовал код доставки сообщения, чтобы убедиться, что если отправитель сам был изолированным объектом, то для синхронных сообщений отправки / ответа это был тот, который ниже в иерархии. Асинхронные сообщения (сигналы) отправляются получателям сообщений, используя отдельные потоки в пуле потоков, которые всю работу доставляют сигналы, поэтому сигналы могут быть отправлены из любого объекта любому получателю в системе. Сигналы могут доставить любые данные сообщения, но ответ невозможен.
Поскольку сообщения могут быть доставлены только в направлении вверх (а сигналы всегда направлены вверх, поскольку они доставляются отдельным потоком, работающим исключительно для этой цели), взаимоблокировки устранены проектно.
Поскольку взаимодействие между потоками осуществляется путем обмена сообщениями с использованием синхронизации Java, условия гонки и проблемы устаревших данных также исключаются при проектировании.
Поскольку любой данный получатель обрабатывает только одно сообщение за раз, и поскольку у него нет других точек входа, все соображения о состоянии объекта исключаются - фактически, объект полностью синхронизирован, и синхронизация не может быть случайно отключена каким-либо методом; нет получателей, возвращающих устаревшие данные кэшированного потока, и никакие установщики, изменяющие состояние объекта, в то время как другой метод действует на него.
Поскольку через этот механизм направляются только взаимодействия между основными компонентами, на практике это масштабируется очень хорошо - эти взаимодействия на практике встречаются не так часто, как я теоретизировал.
Весь проект становится одной из упорядоченной совокупности подсистем , взаимодействующих строго контролируемым образом.
Обратите внимание, что это не используется для более простых ситуаций, когда рабочих потоков, использующих более обычные пулы потоков, будет достаточно (хотя я часто вставляю результаты работника обратно в основную систему, отправляя сообщение IOC). Он также не используется в ситуациях, когда поток отключается и делает что-то полностью независимое от остальной системы, например поток HTTP-сервера. Наконец, он не используется в ситуациях, когда существует координатор ресурсов, который сам не взаимодействует с другими объектами и где внутренняя синхронизация будет выполнять работу без риска тупика.
РЕДАКТИРОВАТЬ: я должен был сказать, что сообщения, которыми обмениваются, как правило, должны быть неизменными объектами; если используются изменяемые объекты, то акт отправки должен рассматриваться как передача, и отправитель должен полностью отказаться от контроля и, предпочтительно, не сохранять никаких ссылок на данные. Лично я использую блокируемую структуру данных, которая блокируется кодом IOC и, следовательно, становится неизменной при отправке (флаг блокировки является энергозависимым).