Шрифт:
Рассмотрим следующий пример:
public class ThreadTest implements Runnable {
// Отдельная группа, в которой будут
// находиться все потоки ThreadTest
public final static ThreadGroup GROUP = new ThreadGroup("Daemon demo");
// Стартовое значение, указывается при создании объекта
private int start;
public ThreadTest(int s) {
start = (s%2==0)? s: s+1;
new Thread(GROUP, this, "Thread "+ start).start;
}
public void run {
// Начинаем обратный отсчет
for (int i=start; i>0; i--) {
try {
Thread.sleep(300);
}
catch (InterruptedException e) {
}
// По достижении середины порождаем
// новый поток с половинным начальным
// значением
if (start>2 && i==start/2)
{
new ThreadTest(i);
}
}
}
public static void main(String s[]) {
new ThreadTest(16);
new DaemonDemo;
}
}
public class DaemonDemo extends Thread {
public DaemonDemo {
super("Daemon demo thread");
setDaemon(true);
start;
}
public void run {
Thread threads[]=new Thread[10]; while (true) {
// Получаем набор всех потоков из
// тестовой группы
int count=ThreadTest.GROUP.activeCount;
if (threads.length<count) threads = new Thread[count+10]; count=ThreadTest.GROUP.enumerate(threads);
// Распечатываем имя каждого потока
for (int i=0; i<count; i++) {
System.out.print(threads[i].getName+", ");
}
System.out.println;
try {
Thread.sleep(300);
}
catch (InterruptedException e) {
}
}
}
}
Пример 12.1.
В этом примере происходит следующее. Потоки ThreadTest имеют некоторое стартовое значение, передаваемое им при создании. В методе run это значение последовательно уменьшается. При достижении половины от начальной величины порождается новый поток с вдвое меньшим начальным значением. По исчерпании счетчика поток останавливается. Метод main порождает первый поток со стартовым значением 16. В ходе программы будут дополнительно порождены потоки со значениями 8, 4, 2.
За этим процессом наблюдает демон -поток DaemonDemo. Этот поток регулярно получает список всех существующих потоков ThreadTest и распечатывает их имена для удобства наблюдения.
Результатом программы будет:
Thread 16,
Thread 16,
Thread 16,
Thread 16,
Thread 16,
Thread 16,
Thread 16,
Thread 16,
Thread 16,
Thread 16, Thread 8,
Thread 16, Thread 8,
Thread 16, Thread 8,
Thread 16, Thread 8,
Thread 16, Thread 8,
Thread 16, Thread 8, Thread 4,
Thread 16, Thread 8, Thread 4,
Thread 8, Thread 4,
Thread 4, Thread 2,
Thread 2,
Пример 12.2.
Несмотря на то, что демон -поток никогда не выходит из метода run, виртуальная машина прекращает работу, как только все не- демон -потоки завершаются.
В примере использовалось несколько дополнительных классов и методов, которые еще не были рассмотрены:
* класс ThreadGroup
Все потоки находятся в группах, представляемых экземплярами класса ThreadGroup. Группа указывается при создании потока. Если группа не была указана, то поток помещается в ту же группу, где находится поток, породивший его.
Методы activeCount и enumerate возвращают количество и полный список, соответственно, всех потоков в группе.
* sleep
Этот статический метод класса Thread приостанавливает выполнение текущего потока на указанное количество миллисекунд. Обратите внимание, что метод требует обработки исключения InterruptedException. Он связан с возможностью активизировать метод, который приостановил свою работу. Например, если поток занят выполнением метода sleep, то есть бездействует на протяжении указанного периода времени, его можно вывести из этого состояния, вызвав метод interrupt из другого потока выполнения. В результате метод sleep прервется исключением InterruptedException.
Кроме метода sleep, существует еще один статический метод yield без параметров. Когда поток вызывает его, он временно приостанавливает свою работу и позволяет отработать другим потокам. Один из методов обязательно должен применяться внутри бесконечных циклов ожидания, иначе есть риск, что такой ничего не делающий поток затормозит работу остальных потоков.
Синхронизация
При многопоточной архитектуре приложения возможны ситуации, когда несколько потоков будут одновременно работать с одними и теми же данными, используя их значения и присваивая новые. В таком случае результат работы программы становится невозможно предугадать, глядя только на исходный код. Финальные значения переменных будут зависеть от случайных факторов, исходя из того, какой поток какое действие успел сделать первым или последним.