Выбрать главу

if (i%10000==0) {

System.out.println(getName()+ " counts " + i/10000);

}

}

}

public String getName() {

return Thread.currentThread().getName();

}

public static void main(String s[]) {

// Подготовка потоков Thread t[] = new Thread[3];

for (int i=0; i<t.length; i++) {

t[i]=new Thread(new ThreadTest(), "Thread "+i);

}

// Запуск потоков

for (int i=0; i<t.length; i++) {

t[i].start();

System.out.println(t[i].getName()+ " started");

}

}

}

В примере используется несколько новых методов класса Thread:

* getName()

Обратите внимание, что конструктору класса Thread передается два параметра. К реализации Runnable добавляется строка. Это имя потока, которое используется только для упрощения его идентификации. Имена нескольких потоков могут совпадать. Если его не задать, то Java генерирует простую строку вида "Thread-" и номер потока (вычисляется простым счетчиком). Именно это имя возвращается методом getName(). Его можно сменить с помощью метода setName().

* currentThread()

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

Результат работы такой программы будет иметь следующий вид:

Thread 0 started

Thread 1 started

Thread 2 started

Thread 0 counts 0

Thread 1 counts 0

Thread 2 counts 0

Thread 0 counts 1

Thread 1 counts 1

Thread 2 counts 1

Thread 0 counts 2

Thread 2 counts 2

Thread 1 counts 2

Thread 2 counts 3

Thread 0 counts 3

Thread 1 counts 3

Thread 2 counts 4

Thread 0 counts 4

Thread 1 counts 4

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

Введем в программу работу с приоритетами, расставим разные значения для разных потоков и посмотрим, как это скажется на выполнении. Изменяется только метод main().

public static void main(String s[]) {

// Подготовка потоков

Thread t[] = new Thread[3];

for (int i=0; i<t.length; i++) {

t[i]=new Thread(new ThreadTest(),

"Thread "+i);

t[i].setPriority(Thread.MIN_PRIORITY +

(Thread.MAX_PRIORITY -

Thread.MIN_PRIORITY)/t.length*i);

}

// Запуск потоков

for (int i=0; i<t.length; i++) {

t[i].start();

System.out.println(t[i].getName()+

" started");

}

}

Формула вычисления приоритетов позволяет равномерно распределить все допустимые значения для всех запускаемых потоков. На самом деле, константа минимального приоритета имеет значение 1, максимального 10, нормального 5. Так что в простых программах можно явно пользоваться этими величинами и указывать в качестве, например, пониженного приоритета значение 3.

Результатом работы будет:

Thread 0 started

Thread 1 started

Thread 2 started

Thread 2 counts 0

Thread 2 counts 1

Thread 2 counts 2

Thread 2 counts 3

Thread 2 counts 4

Thread 0 counts 0

Thread 1 counts 0

Thread 1 counts 1

Thread 1 counts 2

Thread 1 counts 3

Thread 1 counts 4

Thread 0 counts 1

Thread 0 counts 2

Thread 0 counts 3

Thread 0 counts 4

Потоки, как и раньше, стартуют последовательно. Но затем мы видим, что чем выше приоритет, тем быстрее отрабатывает поток. Тем не менее, весьма показательно, что поток с минимальным приоритетом ( Thread 0 ) все же получил возможность выполнить одно действие раньше, чем отработал поток с более высоким приоритетом ( Thread 1 ). Это говорит о том, что приоритеты не делают систему однопоточной, выполняющей единовременно лишь один поток с наивысшим приоритетом. Напротив, приоритеты позволяют одновременно работать над несколькими задачами с учетом их важности.

Если увеличить параметры метода (выполнять 500000 вычислений, а не 50000, и выводить сообщение каждое 1000-е вычисление, а не 10000-е), то можно будет наглядно увидеть, что все три потока имеют возможность выполнять свои действия одновременно, просто более высокий приоритет позволяет выполнять их чаще.

Демон-потоки

Демон -потоки позволяют описывать фоновые процессы, которые нужны только для обслуживания основных потоков выполнения и не могут существовать без них. Для работы с этим свойством существуют методы setDaemon() и isDaemon().

Рассмотрим следующий пример:

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 {