Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Коллоквиум 2012.doc
Скачиваний:
27
Добавлен:
25.08.2019
Размер:
346.11 Кб
Скачать

9. Использование Thread и Runnable. Пул потоков, назначение и принципы реализации

Система многопоточности в Java построена на основе класса Thread и сопутствующего ему интерфейса Runnable. Класс Thread инкапсулирует поток выполнения. Чтобы создать новый поток, программа будет или реализ. интерфейс, или расширять класс Thread. И интерфейс, и класс нах-ся в пакете java.lang, => автоматически доступны всем программам.

Интерфейс Runnable абстрагирует модуль исполняемого кода. Можно создать поток в любом объекте, который реализует данный интерфейс. Runnable определяет только один метод void run(), внутри которого определяется код, образующий новый поток. Метод run() может вызвать другие методы, исп. другие классы и объявлять переменные; метод уст. точку входа для другого, параллельного потока выполнения внутри программы. Поток завершится, когда метод run() вернет рез-т. Класс Thread инкапсулирует поток. Thread опр. несколько методов, которые помогают управлять потоками. Метод start() вызывает метод run(), опр. в интерфейсе. Метод sleep() приостанавливает выполнение потока на опред. момент времени. Когда поток «спит», другой может выполнять свою работу, пока спящий не проснется.  Класс Thread опр-ет два набора конструкторов: один для создания потока в отдельном экземпляре класса Runnable, другой для создания потока в классах, расш. класс Thread. Для обоих наборов конструкторов поток будет создан как поток пользователя, если не оговорено иного.

Чтобы создать поток посредством реализации интерфейса Runnable, нужно: 1) Создать класс, реализ. интерфейс. 2) внутрь метода run(), опред. интерфейсом, поместить код, который должен вып-ся в потоке. 3) Создать экземпляр класса Runnable. 4) Создать объект Thread, передавая его в экземпляр Runnable. 5) Начать выполнение потока посредством метода start() на экземпляре Thread.

Чтобы создать поток посредством расширения класса  Thread, нужно: 1) Создать класс, который будет расширять Thread. 2) Переопределить метод run(), специфицировав код, который будет вып-ся в потоке. 3) Создать экземпляр класса, расш. Thread. 4) Начать вып-ние потока, вызвав метод start() на экземпляре.

Поток является относительно дорогим ресурсом и его создание может быть сопряжено со значительными накладными расходами При разработке программ – серверов зачастую возникает необходимость в параллельной обработке задач или запросов, при этом создание потока каждый раз, когда это нужно оказывается неэффективной стратегией. Для решения подобной проблемы можно воспользоваться «пулом потоков». Пул потоков содержит несколько потоков готовых к работе, которых можно использовать по мере необходимости Пул потоков должен обеспечивать: - возможность быстрого выделения потока для работы - возможность высвобождения потока - систему оповещения о статусе выполняемых задач (для обеспечения обратной связи)

Хотя пул потоков - мощный механизм для структурирования многопоточных приложений, он связан с определенным риском. Приложения, построенные при помощи пулов потоков, подвержены всем тем параллельным рискам, что и любое другое многопоточное приложение, как, например, ошибки синхронизации и взаимоблокировка, и также нескольким другим рискам, специфических также для пулов потоков, таких, как зависимая от пулов взаимоблокировка, пробуксовка ресурсов и рассеяние потока. Одно из преимуществ пулов потоков состоит в том, что они обычно хорошо выполняют операции, имеющие отношение к альтернативным распределяющим механизмам, но это верно только в том случае, если размер пула потоков настроен правильно. Потоки потребляют многочисленные ресурсы, включая память и другие системные ресурсы. Кроме памяти, требующейся для объекта Thread, каждый поток требует двух списков вызовов выполнения, которые могут быть большими. В дополнение к этому, JVM, возможно, создаст "родной" поток для каждого Java-потока, что связано с потреблением дополнительных системных ресурсов. Наконец, поскольку распределяющиеся издержки переключения между потоками малы, для многих потоков переключение процессов может стать значительным замедлением в работе программы. Если пул потоков слишком велик, ресурсы, потребляемые этими потоками, могут в значительной степени повлиять на работу системы. Время будет напрасно потрачено на переключение между потоками, и если потоков больше, чем необходимо, это может вызвать проблемы ресурсного голодания, так как потоки пулов потребляют ресурсы, которые могли бы быть более эффективно использованы другими задачами. Существенный риск в самых разных пулах потоков заключается в утечке потока, которая случается, когда поток удаляется из пула для выполнения задачи, но не возвращается в пул, когда задача выполнена. Во-первых, это происходит, когда задача выдает RuntimeException или Error. Если класс пула их не воспринимает, тогда поток просто прекращается и размер пула потоков сокращается на один. Когда это произойдет достаточное количество раз, пул потоков окажется пустым, и система заблокируется, потому, что нет потоков, доступных для осуществления задач. Задачи, которые постоянно блокируются, например, те, что потенциально ждут ресурсов, которые могут и не стать доступными, или ждут ввода со стороны пользователя, который, возможно, ушел домой, могут также вызвать эффект, эквивалентный утечке потока. Если поток постоянно занимается такой задачей, он фактически удален из пула. Таким задачам следует либо выделять собственный поток, либо ограничить время ожидания. Пул потока - полезный инструмент для организации серверов приложений. Он довольно простой по сути, но есть некоторые моменты, с которыми следует быть осторожными во время применения и использования, такие как взаимоблокировка, пробуксовка ресурсов, и сложности, связанные с wait() и notify(). Если требуется пул потоков для приложения, рассмотрите использование одного из классов Executor из util.concurrent, такой как PooledExecutor, вместо создания нового с нуля.