Программирование на Java

       

Работа с приоритетами


Рассмотрим, как в Java можно назначать потокам приоритеты. Для этого в классе Thread существуют методы getPriority() и setPriority(), а также объявлены три константы:

MIN_PRIORITY MAX_PRIORITY NORM_PRIORITY

Из названия понятно, что их значения описывают минимальное, максимальное и нормальное (по умолчанию) значения приоритета.

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

public class ThreadTest implements Runnable { public void run() { double calc; for (int i=0; i<50000; i++) { calc=Math.sin(i*i); 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-е), то можно будет наглядно увидеть, что все три потока имеют возможность выполнять свои действия одновременно, просто более высокий приоритет позволяет выполнять их чаще.


Содержание раздела