Изменить стиль страницы

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

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

Правда, в классе Thread предусмотрены два средства, позволяющие определить, завершился ли поток. Первым из них является метод is Alive (), объявление которого приведено ниже.final boolean isAlive()

Этот метод возвращает логическое значение true, если поток, для которого он вызывается, все еще исполняется. В противном случае он возвращает логическое значение false. Для того чтобы опробовать метод isAlive () на практике, замените в предыдущей программе класс MoreThreads новой версией, исходный код которой приведен ниже.// Применение метода isAlive().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"); do { System.out.print(" . ") ; try { Thread.sleep(100); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } // Ожидание завершения потоков. } while (mtl.thrd.isAlive() || mt2.thrd.isAlive() || mt3.thrd.isAlive()); System.out.println("Main thread ending."); }}

Эта версия дает такой же результат, как и предыдущая. Единственное отличие состоит в том, что в данном случае ожидание завершения порожденного потока организовано с помощью метода isAlive (). Вторым средством, позволяющим определить, завершился ли поток, является метод join (), объявление которого приведено ниже.final void join() throws InterruptedException

Этот метод ожидает завершения потока, для которого он был вызван. Его имя join выбрано потому, что вызывающий поток ожидает, когда указанный поток присоединится (англ.уши) к нему. Имеется и другой вариант метода j oin (), позволяющий указать максимальное время ожидания момента, когда поток завершится.

В приведенном ниже примере программы наличие метода join () гарантирует, что основной поток завершит работу последним.// Применение метода join().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 JoinThreads { 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"); try { // Ожидание до тех nop, пока указанный метод не завершится. mtl.thrd.join(); System.out.println("Child #1 joined."); mt2.thrd.join() ; System.out.println("Child #2 joined."); mt3.thrd.join(); System.out.println("Child #3 joined."); } 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 #2, count is 0In Child #1, count is 0In Child #3, count is 0In Child #2, count is 1In Child #3, count is 1In Child #1, count is 1In Child #2, count is 2In Child #1, count is 2In Child #3, count is 2In Child #2, count is 3In Child #3, count is 3In Child#1, count is 3In Child #3, count is 4In Child #2, count is 4In Child #1, count is 4In Child #3, count is 5In Child #1, count is 5In Child #2, count is 5In Child #3, count is 6In Child #2, count is 6In Child #1, count is 6In Child #3, count is 7In Child #1, count is 7In Child #2, count is 7In Child #3, count is 8In Child #2, count is 8In Child #1, count is 8In Child #3, count is 9Child #3 terminating.In Child #2, count is 9Child #2 terminating.In Child #1, count is 9Child #1 terminating.Child #1 joined.Child #2 joined.Child #3 joined.Main thread ending.

Как видите, после того как вызываемый метод j oin () возвращает управление, исполнение потока прекращается.Приоритеты потоков

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

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

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

В качестве параметра уровень данному методу передается новый приоритет для потока. Значение параметра уровень должно находиться в пределах от MIN PRIORITY до MAX PRIORITY. В настоящее время этим константам соответствуют числовые значения от 1 до 10. Для того чтобы восстановить приоритет потока по умолчанию, следует указать значение 5, которому соответствует константа N0RM PRI0RITY. Константы, определяющие приоритеты потоков, определены как static final в классе Thread.

Получить текущий приоритет можно с помощью метода getPriorityO из класса Thread, объявляемого следующим образом:final int getPriority()

Ниже приведен пример программы, демонстрирующий использование двух потоков с разными приоритетами. Потоки создаются как экземпляры класса Priority. В методе run () содержится цикл, отсчитывающий число своих шагов. Этот цикл завершает работу, когда значение счетчика достигает 10000000 или же когда статическая переменная stop принимает логическое значение true. Первоначально переменной stop присваивается логическое значение false, но первый же поток, заканчивающий отсчет, устанавливает в ней логическое значение true. В результате второй поток завершится, как только ему будет выделен квант времени. В цикле производится проверка символьной строки в переменной currentName на совпадение с именем исполняемого потока. Если они не совпадают, это означает, что произошло переключение задач. При этом отображается имя нового потока, которое присваивается переменной currentName. Это дает возможность следить за тем, насколько часто каждый поток получает время ЦП. После остановки обоих потоков выводится число шагов, выполненных в каждом цикле.// Демонстрация потоков с разными приоритетами.class Priority implements Runnable { int count; Thread thrd; static boolean stop = false; static String currentName; /* Построение нового потока. Обратите внимание на то, что конструктор не запускает поток на исполнение. */ Priority(String name) { thrd = new Thread(this, name); count = 0; currentName = name; } // начать исполнение нового потока public void run() { System.out.println(thrd.getName() + " starting."); do { count++; if(currentName.compareTo(thrd.getName()) != 0) { currentName = thrd.getName(); System.out.println("In " + currentName); } // Первый же поток, в котором достигнуто значение 10000000, // завершает остальные потоки. } while(stop == false && count < 10000000); stop = true; System.out.println("\n" + thrd.getName() + " terminating."); }}class PriorityDemo { public static void main(String args[]) { Priority mtl = new Priority("High Priority"); Priority mt2 = new Priority("Low Priority"); // задать приоритеты // Поток mtl получает более высокий приоритет, чем поток mt2. mtl.thrd.setPriority(Thread.NORM_PRIORITY+2); mt2.thrd.setPriority(Thread.NORM_PRIORITY-2); // запустить потоки на исполнение mtl.thrd.start(); mt2.thrd.start(); try { mtl.thrd.join(); mt2.thrd.join(); } catch(InterruptedException exc) { System.out.println("Main thread interrupted."); } System.out.println("\nHigh priority thread counted to " + mtl.count); System.out.println("Low priority thread counted to " + mt2.count); }}