Мои Конспекты
Главная | Обратная связь


Автомобили
Астрономия
Биология
География
Дом и сад
Другие языки
Другое
Информатика
История
Культура
Литература
Логика
Математика
Медицина
Металлургия
Механика
Образование
Охрана труда
Педагогика
Политика
Право
Психология
Религия
Риторика
Социология
Спорт
Строительство
Технология
Туризм
Физика
Философия
Финансы
Химия
Черчение
Экология
Экономика
Электроника

При работе с потоками документируйте уровень безопасности



 

То, как класс работает, когда его экземпляры и статические методы одновременно используются в нескольких потоках, является важной частью соглашений, устанав­ливаемых классом для своих клиентов. Если вы не отразите эту сторону поведения класса в документации, использующие его программисты будут вынуждены делать Допущения. И если эти допущения окажутся неверными, полученная программа может иметь либо недостаточную (статья 48), либо избыточную (статья 49) синхронизацию. В любом случае это способно привести к серьезным ошибкам.

Иногда говорится, что пользователи могут сами определить, безопасен ли метод при работе с несколькими потоками, если проверят, присутствует ли модификатор synchronized в документации, генерируемой утилитой Javadoc. Это неверно по

 

 

нескольким причинам. Хотя в ранних версиях утилита javadoc действительно указывала в создаваемом документе модификатор synchronized, это было ошибкой, и в версии 1.2 она была устранена. Наличие в декларации метода модификатора synchronized - это деталь реализации, а не часть внешнего API. Присутствие модификатора не является надежной гарантией того, что метод безопасен при работе с несколькими потоками. От версии к версии ситуация может меняться.

Более того, само утверждение о том, что наличия ключевого слова synchronized достаточно для того, чтобы говорить о безопасности при работе с несколькими пото­ками, содержит в себе распространенную микро-концепцию о категоричности этого свойства. На самом деле, класс может иметь несколько уровней безопасности. Чтобы класс можно было безопасно использовать в среде со многими потоками, в документации к нему должно быть четко указано, какой уровень безопасности он поддерживает.

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

 

· Неизменяемый (immutable). Экземпляры такого класса выглядят для своих клиентов как константы. Никакой внешней синхронизации

не требуется. Примерами являются String, Integer и Biglnteger (статья 13).

· С поддержкой многопоточности (thread-safe). Экземпляры такого класса могут изменяться, однако все методы имеют довольно надежную внутреннюю синхронизацию, чтобы эти экземпляры могли параллельно использовать несколько потоков безо всякой внешней синхронизации. Параллельные вызовы будут обрабатываться последовательно внекотором глобально согласованном порядке. Примеры: Random и java. util. Тiтeг.

· С условной поддержкой многопоточности (conditionally thread-safe). т о же, что и с поддержкой многопоточности, за исключением того, что класс (или ассоциированный класс) содержит методы, которые должны вызываться один за другим без взаимного влияния со стороны других потоков. Для исключения возможности такого влияния клиент должен установить соответствующую блокировку на время выполнения этой последовательности. Примеры: Hashtable и Vector, чьи итераторы требуют внешней синхронизации.

· Совместимый с многопоточностью (thread-compatible). Экземпляры такого класса можно безопасно использовать при работе с параллельными потоками, если каждый вызов метода (а в некоторых случаях, каждую последовательность вызовов) окружить внешней синхронизацией. Примерами являются реализации коллекций общего назначения, такие как ArrayList и HashMap.

 

 

 

 

· Несовместимый с многопоточностью (thread-hostile). Этот класс небезопасен при параллельной работе с несколькими потоками, даже если вызовы всех методов окружены внешней синхронизацией. Обычно несовместимость связана с тем обстоятельством, что эти методы меняют некие статические данные, которые оказывают влияние на другие потоки. К счастью,

в библиотеках платформы Java лишь очень немногие классы и методы несовместимы с многопоточностью. Так, метод System.runFinalizersOnExit несовместим с многопоточностью и признан устаревшим.

 

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

 

Если есть какая-либо опасность того, что хэш-таблица будет изменена из другого потока, то для получения безопасно перечня ее ключей' требуется, чтобы перед вызовом метода keys был блокирован соответствующий экземпляр класса Hashtable и эта блокировка сохранялась до тех пор, пока вы не закончите работу с полученным объектом Enumeration. Описанную схему демонстрирует фрагмент кода:

 

Hashtable h = ... ;

Synchrоnizеd (h) {

for (Enumeration е = h.keys(); е.hаsМогеЕlеmеnts(); )

f(е.пехtЕlеmеnt()); }

 

В версии 1.3 нет такого текста в документации к классу Hashtable. Однако будем надеяться, что эта ситуация скоро будет исправлена. Вообще же в библиотеках для платформы Java безопасность работы с потоками можно было' бы документировать получше.

Хотя объявление об общедоступном блокировании объекта позволяет клиентам выполнять последовательность вызовов как неделимую,. за эту гибкость приходится платить. Клиент, имеющий злой умысел, может предпринять атаку "отказ в обслужи­вании" (denial-of-service, DOS attack), установив блокировку объекта:

// DOS-атака

synchrоnizеd (importantObject) { Тhгеаd.slеер(Iпtеgег,МАХ_VАLUЕ);

// ВЫВОДИТ из строя iтрогtапtОЬjесt

 

 

 

 

Если вас беспокоят DОS - атаки, то используйте для синхронизации операций

закрытый объект блокировки (private lock object):

 

// Идиома закрытого объекта блокировки, препятствует DOS-атаке

private Object lock = new Object();

public void foo() {

synchrоnizеd (lock) { }

}

 

Поскольку создаваемая этим объектом блокировка недоступна клиентам, данный объект-контейнер неуязвим для представленной ранее DОS - атаки. Заметим, что классы с условной поддержкой многопоточности всегда неустойчивы по отношению к такой атаке, в документации к ним должно быть указано, что выполнение последо­вательности операций атомарным образом требует получения блокировки. Однако классы с поддержкой многопоточности могут быть защищены от DОS -атаки при помощи идиомы закрытого объекта блокировки.

Применение внутренних объектов для блокировки особенно подходит классам, предназначенным для наследования (статья 15), таким как класс WorkQueue (статья 49). Если бы суперкласс использовал для блокировки собственные экземпляры, подкласс мог бы непреднамеренно повлиять на их работу. Применяя одну и ту же блокировку для разных целей, суперкласс и подкласс стали бы, в конце концов, "наступать друг другу на пятки".

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