Главная » Java » Использование объектов Runnablе Java

0

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

public void run();

Класс Thread реализует интерфейс Runnablе, поскольку поток сам по себе способен определять план работы, подлежащей выполнению.

Вы уже видели, как класс Thread может быть расширен с целью описания Конкретных действий, которые должны выполняться потоком, но такой подход во многих случаях неуклюж и неэффективен. Во-первых, допускается наследовать только один класс – если вы расширяете класс Thread, чтобы обеспечить возможность работы производного класса в виде потока, вы не сможете, даже если очень захотите, унаследовать при этом какой бы то ни было другой класс. Во-вторых, если производный класс нуждается Только в реализации возможности Выполнения, наследование всех "внутренностей" класса Thread чересчур накладно и просто излишне.

Реализация интерфейса Runnable во многих случаях представляется более Простым решением. Код объекта Runnablе может быть выполнен в его собственном Потоке при передаче объекта конструктору Thread. Если объект Thread конструируется на основе объекта Runnablе, из тела реализованного метода Thread. гип вызывается метод гип объекта Runnablе.

Ниже приведен текст версии класса РingPong, реализующей интерфейс Runnablе. Сопоставив оба варианта, вы убедитесь, что они почти совпадают. Основные различия касаются заголовка объявления (теперь класс RunРingPong реализует интерфейс Runnablе, а не расширяет класс Thread) и содержимого Метода main.

public class RunРingPong implements Runnable {

         private String word;               // Слово, подлежащее выводу на экран

         private int delay;                    // Величина временной задержки

RunРingPong(String whatTosay, int delayTime) {

word = whatToSay;

delay = delayTime;

}

public void run() {

 try {

for (;;) {

system.out.print(word + " ");

Thread.sleep(delay); // Приостановить выполнение

}

} catch (InterruptedException е) {

 return; // Завершить поток

}

}

public static void main(String[] args) {

Runnable ping = new RunРingPong("ping",33);

Runnable pong = new RunРingPong("PONG", 100);

new Thread(ping).start();

new Thread(pong).start();

}

}

пределен новый класс, реализующий интерфейс Runnablе. Реализация метода совпадает с прежней, предусмотренной в классе РingPong. В теле метода mаin создаются два объекта RunРingPong с разными уровнями "активности"; для каждого из них конструируется объект потока и немедленно запускаться на выполнение.

Существует четыре перегруженные версии конструктора класса Thread, позволяющие передать в качестве параметра объект Runnablе.

public Thread(Runnable target)

Создает новый объект Thread, использующий метод гип объекта target. public Thread(Runnable target, String name)

Создает новый объект Thread с заданным именем name, использующий метод гип объекта target.

public Thread(ThreadGroup group, Runnable target)

Создает новый объект Thread в указанном объекте ThreadGroup, использующий метод гип объекта target. (За сведениями о классе ThreadGroup обращайтесь к разделу 10.11 на странице 277.)

public Thread(ThreadGroup group, Runnable target, String name) Создает новый объект Thread с заданным именем name в указанном объекте ThreadGroup, использующий метод гип объекта target.

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

Рассмотрим, например, сервер печати, который получает задания и посылает их на принтер. Клиенты сервера, желающие выполнить печать, вызывают метод print. Функция метода заключается только в том, чтобы помещать задания в очередь; затем отдельный поток извлекает их из очереди и посылает на принтер. Это позволяет клиентам отправлять задания на печать, не дожидаясь завершения выполнения других заданий.

class PrintServer implements Runnable {

 private Queue requests = new Queue();

public PrintServer() {

new Thread(this).start(); }

 

public void print(PrintJob job) {

requests.add(job);

}

public void run() {

for(; ;)

realPrint((PrintJob)requests.take()); }

private void realPrint(PrintJob job) {

// Выполнить фактическую функцию печати

      }

} Конструктор объекта PrintServer создает новый объект Thread, ответственный за выполнение печати заданий, и передает последнему ссылку на себя как экземпляр Runnablе. Старт потока в конструкторе, выполняемый подобным образом, в общем случае оказывается довольно рискованной операцией, если класс впоследствии будет расширяться, – поток таким образом сможет обращаться к полям объекта еще до момента завершения выполнения конструктора производного класса.

Объект очереди requests "заботится" о синхронизации работы различных потоков .

Вам может показаться странным тот факт, что после создания объекта потока мы не используем ссылки на него. Не означает ли это, что объект сразу после построения поступает в распоряжение процесса сборки мусора? Нет, такое предположение не соответствует действительности. Хотя мы явно не сохраняем ссылки на поток, он самостоятельно делает это с помощью объекта ThreadGroup.

План работы, определяемый в теле метода гип, обычно отличается достаточно частным характером и предназначается конкретному исполнителю. Однако run как часть интерфейса обладает признаком public и может быть вызван любым кодом, обладающим достаточными правами доступа к объекту, – это наверняка не то, что предусматривается при нормальных обстоятельствах. Например, мы определенно не хотим, чтобы клиенты обращались к методу run класса PrintServer. Одно из решений состоит в использовании статического метода Thread.currentThread для распознавания "личности" потока, вызвавшего run, и сопоставления его с тем, которому подобное обращение позволено. Но существует и более простой подход – не реализовывать интерфейс Runnablе, а объя-

Внутренний объект типа Runnablе. Следуя сказанному, можно переписать класса PrintServer следующим образом:

class PrintServer2 {

pr;vate Queue requests = new Queue();

public PrintServer2() {

Runnable service = new Runnable() {

public void run() {

for(; ;)

 realPrint((PrintJob)requests.take());

            }

};

new Thread(service).start();

}

public void print(PrintJob job) {

requests.add(job);

}

private void  realprint(PrintJob job) {

// выполнить фактическую функцию печати

}

Упражнение 10.2. Исправьте первую версию класса PrintServer таким образом, чтобы возможность выполнения run предоставлялась только тому потоку, который создается в конструкторе класса. Используйте упомянутый выше способ идентификации потока, связанный с использованием Thread. currentThread.

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

По теме:

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