355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Герберт Шилдт » Java: руководство для начинающих (ЛП) » Текст книги (страница 23)
Java: руководство для начинающих (ЛП)
  • Текст добавлен: 6 октября 2016, 05:33

Текст книги "Java: руководство для начинающих (ЛП)"


Автор книги: Герберт Шилдт



сообщить о нарушении

Текущая страница: 23 (всего у книги 36 страниц)

class Help { String helpfile; // Имя файла со справочной информацией Help(String fname) { helpfile = fname;

} 7. Имя файла со справочной информацией передается конструктору класса Help и запоминается в переменной экземпляра helpfile. А поскольку каждый экземпляр класса Help содержит отдельную копию переменной helpf ile, то каждый из них может взаимодействовать с отдельным файлом. Это дает возможность создавать отельные наборы справочных файлов на разные темы. 8. Добавьте в класс Help метод helpon (), код которого приведен ниже. Этот метод извлекает справочную информацию по заданной теме.

// отобразить справочную информацию по заданной теме boolean helpon(String what) { int ch; String topic, info; // открыть справочный файл try (BufferedReader helpRdr = new BufferedReader(new FileReader(helpfile))) { do { // читать символы до тех пор, пока не встретится знак # ch = helpRdr.read(); // а теперь проверить, совпадают ли темы if(ch == '#') { topic = helpRdr.readLine(); if(what.compareTo(topic) == 0) { // found topic do { info = helpRdr.readLine(); if(info != null) System.out.println(info); } while((info != null) && (info.compareTo("") != 0)); return true; } } } while(ch != -1); } catch(IOException exc) { System.out.println("Error accessing help file."); return false; } return false; // тема не найдена

} 9. Прежде всего обратите внимание на то, что в методе helpon () обрабатываются все исключения, связанные с вводом-выводом, поэтому в заголовке метода не указано ключевое слово throws. Благодаря такому подходу упрощается разработка методов, в которых используется метод helpon (). В вызывающем методе достаточно обратиться к методу helpon (), не заключая его вызов в блок try/catch. 10. Для открытия файла со справочной информацией служит класс FileReader, оболочкой которого является класс Buf feredReader. В справочном файле содержится текст, и поэтому справочную систему удобнее локализовать через символьные потоки ввода-вывода. 11. Метод helpon ( действует следующим образом. Символьная строка, содержащая название темы, передается этому методу в качестве параметра. Метод открывает сначала файл со справочной информацией. Затем в файле производится поиск, т.е. проверяется совпадение содержимого переменной what и названия темы. Напомним, что в файле заголовок темы предваряется символом #, поэтому метод сначала ищет данный символ. Если символ найден, производится сравнение следующего за ним названия темы с содержимым переменной what. Если сравниваемые строки совпадают, то отображается справочная информация по данной теме. И если заголовок темы найден, то метод helpon () возвращает логическое значение true, иначе – логическое значение false. 12. В классе Help содержится также метод getSelectionO, который предлагает задать тему и возвращает строку, введенную пользователем.

// получить тему String getSelectionO { String topic = ""; BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Enter topic: ") ; try { topic = br.readLine(); } catch(IOException exc) { System.out.println("Error reading console."); } return topic;

} 13. В теле этого метода сначала создается объект типа Buf feredReader, который связывается с потоком вывода System, in. Затем в нем запрашивается название темы, которое принимается и далее возвращается вызывающей части программы. 14. Ниже приведен весь исходный код программы, реализующей справочную систему, находящуюся на диске.

/* Пример для опробования 10.2. Справочная система, находящаяся на диске. Для компиляции этой программы требуется JDK 7 или более поздняя версия данного комплекта.

/ import java.io.;

/ В классе Help открывается файл со справочной информацией, производится поиск названия темы, а затем отображается справочная информация по этой теме. Обратите внимание на то, что в этом классе поддерживаются все исключения, освобождая от этой обязанности вызывающий код. / class Help { String helpfile; // Имя файла со справочной информацией Help(String fname) { helpfile = fname; } // отобразить справочную информацию по заданной теме boolean helpon(String what) { int ch; String topic, info; // открыть справочный файл try (BufferedReader helpRdr = new BufferedReader(new FileReader(helpfile))) { do { // читать символы до тех пор, пока не встретится знак # ch = helpRdr.read(); // а теперь проверить, совпадают ли темы if(ch =='#') { topic = helpRdr.readLine(); if(what.compareTo(topic) == 0) { // тема найдена do { info = helpRdr.readLine(); if(info != null) System.out.println(info); } while((info != null) && (info.compareTo("") != 0)); return true; } } } while(ch != -1); } catch(IOException exc) { System.out.println(«Error accessing help file.»); return false; } return false; // тема не найдена } // получить тему String getSelection() { String topic = ""; BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); System.out.print("Enter topic: "); try { topic = br.readLine(); } catch(IOException exc) { System.out.println(«Error reading console.»); } return topic; }

}

// продемонстрировать справочную систему, находящуюся на диске class FileHelp { public static void main(String args[]) { Help hlpobj = new Help("helpfile.txt"); String topic; System.out.println("Try the help system. " + "Enter ’stop' to end."); do { topic = hlpobj.getSelection (); if(!hlpobj.helpon(topic)) System.out.println("Topic not found.n"); } while(topic.compareTo("stop") != 0); }

} ``` Упражнение для самопроверки

по материалу главы 10

Для чего в Java определены как байтовые, так и символьные потоки?

Как известно, ввод-вывод данных на консоль осуществляется в текстовом виде. Почему же в Java для этой цели используются байтовые потоки?

Как открыть файл для чтения байтов?

Как открыть файл для чтения символов?

Как открыть файл для ввода-вывода с произвольным доступом?

Как преобразовать числовую строку "123.23" в двоичный эквивалент?

Напишите программу, которая будет копировать текстовые файлы. Видоизмените ее таким образом, чтобы все пробелы заменялись дефисами. Используйте при написании программы классы, представляющие байтовые потоки, а также традиционный способ закрытия файла явным вызовом метода close ().

Перепишите программу, созданную в ответ на предыдущий вопрос, таким образом, чтобы в ней использовались классы, представляющие символьные потоки. На этот раз воспользуйтесь оператором try с ресурсами для автоматического закрытия файла.

К какому типу относится поток System. in?

Что возвращает метод read () из класса InputStream по достижении конца потока?

Поток какого типа используется для чтения двоичных данных?

Классы Reader и Writer находятся на вершине иерархии классов _ .

Оператор try без ресурсов служит для __ .

Если для закрытия файла используется традиционный способ, то это лучше всего делать в блоке finally. Верно или неверно?

Глава 11 Многопоточное программирование

Основные навыки и понятия

Общее представление о многопоточной обработке

Класс Thread и интерфейс Runnable

Создание потока

Создание нескольких потоков

Определение момента завершения потока

Использование приоритетов потоков

Представление о синхронизации потоков

Применение синхронизированных блоков

Взаимодействие потоков

Приостановка, возобновление и остановка потоков

Среди многих замечательных свойств языка Java особое место принадлежит поддержке многопоточного программирования. Многопоточная программа состоит из двух или более частей, выполняемых параллельно. Каждая часть такой программы называется потоком и определяет отдельный путь выполнения команд. Таким образом, многопоточная обработка является особой формой многозадачности. Общее представление о многопоточной обработке

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

Поток представляет собой координируемую единицу исполняемого кода. Своим происхождением этот термин обязан понятию “поток исполнения”. При организации многозадачности на основе потоков у каждого процесса должен быть по крайней мере один поток, хотя их может быть и больше. Это означает, что в одной программе одновременно можно решать две и более задачи. Например, текст может форматироваться в редакторе текста одновременно с его выводом на печать, при условии, что оба эти действия выполняются в двух отдельных потоках. Несмотря на то что программы на Java выполняются в среде, поддерживающей многозадачность на основе процессов, в самих программах управлять процессами нельзя. В них можно управлять только потоками.

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

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

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

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

Если вы пишете программы для таких операционных систем, как Windows, то принципы многопоточного программирования вам должны быть уже знакомы. Но то обстоятельство, что в Java имеются языковые средства для поддержки потоков, упрощает организацию многопоточной обработки, поскольку избавляет от необходимости реализовывать ее во всех деталях. Класс Thread и интерфейс Runnable

В основу системы многопоточной обработки в Java положены класс Thread и интерфейс Runnable, входящие в пакет java. lang. Класс Thread инкапсулирует поток исполнения. Для того чтобы образовать новый поток, нужно создать класс, являющийся подклассом Thread или реализующий интерфейс Runnable.

В классе Thread определен ряд методов, позволяющих управлять потоками. Некоторые из этих наиболее употребительных методов описаны ниже. По мере их представления в последующих примерах программ вы ознакомитесь с ними поближе. Метод Описание final String getName() Получает имя потока final int getPriority() Получает приоритет потока final boolean isAliveO Определяет, выполняется ли поток final void join() Ожидает завершения потока void run() Определяет точку входа в поток static void sleep(long миллисекунд) Приостанавливает исполнение потока на указанное число миллисекунд void start() Запускает поток, вызывая его метод run ()

В каждом процессе имеется как минимум один поток исполнения, который называется основным потоком. Он получает управление уже при запуске программы.

Следовательно, во всех рассматривавшихся до сих пор примерах программ использовался основной поток. От основного потока могут быть порождены другие, подчиненные потоки. Создание потока

Для того чтобы создать поток, нужно построить объект типа Thread. Класс Thread инкапсулирует объект, который может стать исполняемым. Как пояснялось ранее, пригодные для исполнения объекты можно создавать в Java двумя способами:

реализуя интерфейс Runnable;

создавая подкласс класса Thread.

В большинстве примеров, представленных в этой главе, будет применяться первый способ. Хотя в примере для опробования 11.1 будет продемонстрировано, каким образом поток реализуется путем расширения класса Thread. Но независимо от выбранного способа создание экземпляра потока, организация доступа к нему и управление потоком осуществляется средствами класса Thread. Единственное отличие обоих способов состоит в том, как создается класс, активизирующий поток.

Интерфейс Runnable дает абстрактное описание единицы исполняемого кода. Для формирования потока подходит любой объект, реализующий этот интерфейс. В интерфейсе Runnable объявлен только один метод, run (): public void run()

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

После создания класса, реализующего интерфейс Runnable, следует создать экземпляр объекта типа Thread на основе объекта данного класса. В классе Thread определен ряд конструкторов. В дальнейшем будет использоваться следующий конструктор: Thread(Runnable threadOb)

В качестве параметра threadOb этому конструктору передается экземпляр класса, реализующего интерфейс Runnable. Благодаря этому определяется место для исполнения потока.

Созданный поток не начнет исполнение до тех пор, пока не будет вызван метод start (), объявленный в классе Thread. По существу, единственным назначением метода start () является вызов метода run (). А объявляется метод start () следующим образом: void start()

Ниже приведен пример программы, в которой создается и запускается на исполнение новый поток. // Создание потока путем реализации интерфейса Runnable, class MyThread implements Runnable { String thrdName; // Объекты типа MyThread выполняются в отдельных потоках, так как // класс MyThread реализует интерфейс Runnable. MyThread(String name) { thrdName = name; } // Точка входа в поток, public void run() { // Здесь начинают исполняться потоки. System.out.println(thrdName + " starting."); try { for(int count=0; count < 10; count++) { Thread.sleep(400); System.out.println("In " + thrdName + ", count is " + count); } } catch(InterruptedException exc) { System.out.println(thrdName + " interrupted."); } System.out.println(thrdName + " terminating."); } } class UseThreads { public static void main(String args[]) { System.out.println("Main thread starting."); // сначала построить объект типа MyThread MyThread mt = new MyThread("Child #1"); // Создание исполняемого объекта. // далее сформировать поток из этого объекта Thread newThrd = new Thread(mt); // Формирование потока из этого объекта. // и, наконец, начать исполнение потока newThrd.start О; // Начало исполнения потока. for(int i=0; i<50; i++) { System.out.print(".") ; try { Thread.sleep(100) ; } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } System.out.println("Main thread ending."); } }

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

В теле метода run () присутствует цикл, в котором производится отсчет от 0 до 9. Обратите внимание на вызов метода sleep (). Этот метод приостанавливает поток, из которого он был вызван на указанное число миллисекунд. Ниже приведена общая форма объявления данного метода. static void sleep(long миллисекунд) throws InterruptedException

Единственный параметр метода sleep () задает время задержки, определяемое числом миллисекунд. Как следует из объявления этого метода, в нем может быть сгенерировано исключение InterruptedException. Следовательно, его нужно вызывать в блоке try. Имеется и другой вариант метода sleep (), позволяющий точнее указывать время задержки в миллисекундах и дополнительно в наносекундах. Когда метод sleep () вызывается в методе run (), исполнение потока приостанавливается на 400 миллисекунд на каждом шаге цикла. Благодаря этому поток исполняется достаточно медленно, чтобы можно проследить за ним.

В методе main () создается новый объект типа Thread. Для этой цели служит приведенная ниже последовательность операторов. // сначала построить объект типа MyThread MyThread mt = new MyThread("Child #1"); // далее сформировать поток из этого объекта Thread newThrd = new Thread(mt); // и, наконец, начать исполнение потока newThrd.start();

Как видите, сначала создается объект типа MyThread, а затем он используется для построения объекта типа Thread. Его можно передать конструктору класса Thread в качестве параметра, поскольку класс MyThread реализует интерфейс Runnable. И наконец, начинается исполнение нового потока, для чего вызывается метод start (), что приводит к вызову метода run () из порожденного потока. После вызова метода start () управление возвращается к методу main (), где начинается выполнение цикла for. Этот цикл повторяется 50 раз, приостанавливая на 100 миллисекунд исполнение потока на каждом своем шаге. Оба потока продолжают исполняться, разделяя ресурсы

ЦП в однопроцессорной системе до тех пор, пока циклы в них не завершатся. Ниже приведен результат выполнения данной программы. Вследствие отличий в вычислительных средах у вас может получиться несколько иной результат. Main thread starting. .Child #1 starting. ....In Child #1, count is 0 .....In Child #1, count is 1 .....In Child #1, count is 2 .....In Child #1, count is 3 .....In Child #1, count is 4 .....In Child #1, count is 5 .....In Child #b count is 6 .....In Child #1, count is 7 .....In Child #1, count is 8 .....In Child #1 count is 9 Child #1 terminating. Main thread ending

В рассматриваемом здесь первом примере организации многопоточной обработки любопытно также отметить следующее обстоятельство: для демонстрации того факта, что основной и порожденный потоки исполняются одновременно, необходимо задержать завершение метода main () до тех пор, пока не окончится порожденный поток mt. В данном примере это достигается благодаря отличиям во временных характеристиках обоих потоков. Вызовы метода sleep () из цикла for в методе main () приводят в итоге к задержке на 5 секунд (50 шагов цикла х 100 миллисекунд), тогда как общая задержка с помощью того же самого метода в аналогичном цикле в методе run () составляет лишь 4 секунды (10 шагов цикла х 400 миллисекунд). Поэтому метод run () завершится приблизительно на 1 секунду раньше, чем метод main (). В итоге основной и порожденный потоки будут выполняться параллельно до тех пор, пока не завершится порожденный поток mt. А приблизительно через одну секунду завершится и основной поток в методе main ().

Отличий во временнь/х характеристиках обоих потоков в данном и ряде последующих простых примеров оказывается достаточно для того, чтобы основной поток в методе main () завершился последним, но на практике этого, как правило, оказывается недостаточно. В Java предоставляются намного более совершенные способы, позволяющие организовать ожидание завершения потока. Далее в этой главе будет продемонстрирован более совершенный способ организации ожидания одним потоком завершения другого.

И последнее замечание: многопоточная программа обычно разрабатывается с таким расчетом, чтобы последним завершал свою работу основной поток. Как правило, выполнение программы продолжается до тех пор, пока все потоки не завершат работу. Поэтому завершение основного потока является не требованием, а рекомендуемой для наследования нормой, особенно для тех, кто лишь начинает осваивать многопоточное программирование. Несложные усовершенствования многопоточной программы

Рассмотренная выше многопоточная программа вполне работоспособна, тем не менее ей не помешает небольшая доработка, повышающая ее эффективность. Во– первых, можно сделать так, чтобы поток начинал исполняться сразу после создания. Эта цель достигается созданием экземпляра объекта типа Thread в конструкторе класса MyThread. И во-вторых, нет никакой нужды хранить в объекте типа MyThread имя потока, но присвоить имя потоку при его создании. Эту задачу позволяет решить следующий вариант конструктора Thread: Thread(Runnable threadOb, String имя)

где имя обозначает конкретное наименование потока.

Получить имя потока можно, используя метод getName (), определенный в классе Thread. Ниже приведено объявление этого метода. final String getName()

В приведенной ниже программе имя присваивается потоку после его создания с помощью метода setName (). И хотя в этом нет особой необходимости, такое решение выбрано лишь для того, чтобы продемонстрировать возможности класса Thread. Объявление метода setName () имеет следующий вид: final void setName(String имя_потока)

где имя_потока обозначает имя, которое присваивается потоку.

Ниже приведена видоизмененная версия предыдущей программы. // Видоизменение класса MyThread. class MyThread implements Runnable { Thread thrd; // В этой переменной хранится ссылка на поток. // построить новый поток MyThread(String name) { thrd = new Thread(this, name); // Поток именуется при его создании, thrd.start() ; // Начало исполнения потока. } // начать исполнение нового потока public void run() { System.out.println(thrd.getName() + " starting."); try { for (int count=0; countclO; count++) { Thread.sleep(400); System.out.println("In " + thrd.getName() + ", count is " + count)'; } } catch(InterruptedException exc) { System.out.println(thrd.getName() + " interrupted."); } System.out.println(thrd.getName() + " terminating."); } } class UseThreadsImproved { public static void main(String args[]) { System.out.println("Main thread starting."); // Теперь поток начинается при его создании. MyThread mt = new MyThread("Child #1"); for (int i=0; i < 50; i++) { System.out.print(".") ; try { Thread.sleep(100) ; } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } System.out.println("Main thread ending."); } }

Эта версия программы дает такой же результат, как и предыдущая. Обратите внимание на то, что ссылка на поток хранится в переменной thrd экземпляра класса MyThread.

Пример для опробования 11.1. Расширение класса Thread

Реализация интерфейса Runnable – это лишь один из способов получения экземпляров потоковых объектов. Другой способ состоит в создании подкласса, производного от класса Thread. В этом проекте будет продемонстрировано, каким образом расширение класса Thread позволяет реализовать такие же функциональные возможности, как и у рассмотренной выше программы UseThreadsImproved.

В подклассе, производном от класса Thread, нужно переопределить метод run (), который является точкой входа в новый поток. Для того чтобы начать исполнение нового потока, следует вызвать метод start (). Можно также переопределить и другие методы из класса Thread, но делать это не обязательно.

Последовательность действий

Создайте файл ExtendThread.java. Скопируйте в этот файл исходный код второго рассмотренного ранее примера программы (файл UseThreadsImproved. java).

Измените объявление класса MyThread. Теперь он должен быть подклассом, производным от класса Thread, как показано ниже. class MyThread extends Thread {

Удалите следующую строку кода: Thread thrd; Переменная thrd уже не нужна, поскольку класс MyThread включает в себя экземпляр класса Thread и может ссылаться на самого себя.

Внесите в конструктор класса Thread следующие изменения: // построить новый поток. MyThread(String name) { super(name); // присвоить потоку имя start(); // начать поток } Как видите, в данном конструкторе присутствует ключевое слово super, которое используется для вызова следующего варианта конструктора Thread: Thread(String имя); где имя обозначает присваиваемое потоку конкретное имя.

Внесите приведенные ниже изменения в метод run (), чтобы он вызывал метод getName () непосредственно, не предваряя его именем переменной thrd. // начать исполнение нового метода public void run () { System.out.println(getName() + " starting."); try { for(int count=0; count < 10; count++) { Thread.sleep(400); System.out.println("In " + getName() + ", count is " + count); } } catch(InterruptedException exc) { System.out.println(getName() + " interrupted."); } System.out.println(getName() + " terminating."); }

Ниже приведен весь исходный код программы, в которой вместо реализации интерфейса Runnable используется подкласс, производный от класса Thread. Выполнение этой программы дает такой же результат, как и предыдущие ее версии. /* Пример для опробования 11.1. Расширение класса Thread. */ class MyThread extends Thread { // построить новый поток MyThread(String name) { super(name); // присвоить потоку имя start (); // начать поток } // начать исполнение нового потока public void run() { System.out.println(getName() + " starting."); try { for(int count=0; count < 10; count++) { Thread.sleep(400); System.out.println("In " + getName() + ", count is " + count); } } catch(InterruptedException exc) { System.out.println(getName() + " interrupted."); } System.out.println(getName() + " terminating."); } } class ExtendThread { public static void main(String args[]) { System.out.println("Main thread starting."); MyThread mt = new MyThread("Child #1"); for(int i=0; i < 50; i++) { System.out.print("."); try { Thread.sleep(100); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } System.out.println("Main thread ending."); } } Создание нескольких потоков

В предыдущем примере был создан только один порожденный поток. Но в программе можно породить столько потоков, сколько требуется. Например, в приведенной ниже программе формируются три порожденных потока. // Создание нескольких потоков. class MyThread implements Runnable { Thread thrd; // построить новый поток MyThread(String name) { thrd = new Thread(this, name); thrd.start(); // начать поток } // начать исполнение нового потока public void run() { System.out.println(thrd.getName() + " starting."); try { for(int count=0; count < 10; count++) { Thread.sleep(400); System.out.println("In " + thrd.getName() + ", count is " + count); } } catch(InterruptedException exc) { System.out.println(thrd.getName() + " interrupted."); } System.out.println(thrd.getName() + " terminating."); } } class MoreThreads { public static void main(String args[]) { System.out.println("Main thread starting."); // Создание и запуск на исполнение трех потоков. MyThread mtl = new MyThread("Child #1"); MyThread mt2 = new MyThread("Child #2"); MyThread mt3 = new MyThread("Child #3"); for (int i=0; i < 50; i++) { System.out.print("."); try { Thread.sleep(100); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } } System.out.println("Main thread ending."); } }

Ниже приведен результат выполнения данной программы. Main thread starting. Child #1 starting. .Child #2 starting. Child #3 starting. ...In Child #3, count is О In Child #2, count is 0 In Child #1, count is 0 ....In Child #1, count is 1 In Child #2, count is 1 In Child #3, count is 1 ....In Child #2, count is 2 In Child #3, count is 2 In Child #1, count is 2 ...In Child #1, count is 3 In Child #2, count is 3 In Child #3, count is 3 ....In Child #1, count is 4 In Child #3, count is 4 In Child #2, count is 4 ....In Child #1, count is 5 In Child #3, count is 5 In Child #2, count is 5 ...In Child #3, count is 6 .In Child #2, count is 6 In Child #1, count is 6 ...In Child #3, count is 7 In Child #1, count is 7 In Child #2, count is 7 ....In Child #2, count is 8 In Child #1, count is 8 In Child #3, count is 8 ....In Child #1, count is 9 Child #1 terminating. In Child #2, count is 9 Child #2 terminating. In Child #3, count is 9 Child #3 terminating. Main thread ending.

Как видите, после запуска на исполнение все три потока совместно используют ресурсы ЦП. Следует иметь в виду, что потоки в данном примере запускаются на исполнение в том порядке, в каком они были созданы. Но так происходит не всегда. Исполняющая система Java сама планирует исполнение потоков. Вследствие отличий в вычислительных средах у вас может получиться несколько иной результат. Определение момента завершения потока

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


    Ваша оценка произведения:

Популярные книги за неделю