Главная » Java » Синхронизация и состязания в Java

0

 

   Классы байтовых и символьных потоков придерживаются определенной политики синхронизации (synchronization policy), хотя достигают поставленных целей по-разному. Поведение потоковых классов в отношении разрешения возможных ситуаций, связанных с состязаниями, оговорено в соответствующих спецификациях отнюдь не исчерпывающе, но его можно достаточно полно описать следующим образом.

  Каждый класс байтового потока, выполняя операции, которые должны протекать при   отсутствии   внешних   воздействий   со   стороны   иных   потоков   вычислений (threads), осуществляет синхронизацию текущего объекта потока данных. Это позволяет нескольким потокам вычислений успешно использовать один и тот же поток данных, вызывая требуемые методы объекта потока. Если, например, два независимых потока вычислений пытаются осуществлять ввод информации из одного и того же потока данных порциями по n байтов, в результате каждой из операций ввода будут получены массивы длиной до n байтов, данные в которых соответствуют непрерывным последовательностям байтов в потоке-источнике. Аналогично, если два потока вычислений осуществляют вывод информации в один и тот же поток данных, байты, сохраняемые каждым потоком вычислений, будут размещены в потоке данных в виде непрерывных последовательностей, не перемежаемых байтами, которые выводятся другими потоками вычислений.

   Политика синхронизации, применяемая в классах символьных потоков, отличается от той, которая реализована в классах байтовых потоков. Символьные потоки выполняют синхронизацию protected-поля lock объекта потока, в котором хранится ссылка на объект потока как таковой. В обоих классах, Reader и Writer, помимо того, предусмотрен protected-конструктор, в качестве параметра получающий объект, ссылка на который будет присвоена полю lock создаваемого объекта потока. В некоторых классах поле 1 оск ссылается на другой объект. Так, например в   объекте   класса   StringWri ter,   выполняющего   запись   символов   в   объект StringBuffer, поле lock указывает именно на объект StringBuffer. При создании типа Reader или Writer следует присваивать полю lock ссылку на соответствующий объект, если this по каким-то соображениям для этой цели не подходит-Если же наследуется существующий тип Reader или Writer, надлежит всегда синхронизировать поле lock, но не объект this.

   Во многих случаях один определенный объект потока служит простой оболочкой для экземпляра другого потока и передает последнему основные функции обработки данных, формируя цепочку связанных потоков, как в ситуации c точками Filter. Теперь характеристики поведения "внешнего" потока, касаю-цтиеся синхронизации, будут целиком зависеть от соответствующих свойств "внутреннего" потока. Сложности могут возникнуть только в том случае, если «внешнему" потоку необходимо выполнять некоторые дополнительные действия, томарным образом связанные с основными функциями обработки данных. Однако в большинстве ситуаций фильтрованные потоки, например, просто манипу-лИруют данными перед выводом (записью) их во "внешний" поток либо после ввода (чтения) из "внешнего" потока, и вопросы синхронизации никакой проблемы не представляют.

  Большинство операций ввода способно выполнять блокировку до тех пор, пока в потоке есть доступные данные, а операции поточного вывода так же, как правило, осуществляют блокировку в процессе передачи информации — первым источником или конечным получателем данных, например, может быть поток, связанный с сетевым соединением. С целью обеспечения быстрой реакции потока вычислений, который выполняет подобные операции ввода-вывода, связанные с блокировками, на внешние запросы, касающиеся немедленного завершения операции, в реализации метода ввода-вывода может быть предусмотрен отклик на уведомление о прерывании, посланное объектом Thread (см раздел 10.8.1), находящий выражение в разблокировании потока и выбрасывании исключения типа interruptedlOException. Объект исключения может, скажем, представить пользователю сведения о количестве байтов, переданных до возникновения события прерывания, — если, разумеется, об этом "позаботится" код, генерирующий исключение.

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

  Даже если отклик на уведомление о прерывании не может быть обеспечен непосредственно во время протекания операции ввода-вывода, во многих системах предусматривается проверка наличия такого уведомления до начала и после завершения операции с возможностью выбрасывания исключения InterruptedlOException, когда уведомление действительно послано. Помимо того, если поток вычислений блокирует поток данных, когда тот закрывается другим потоком вычислений, в большинстве случаев блокировка снимается и генерируется исключение типа l0Exception.

 

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии