Реализация многопоточности в Java
должны воспользоваться классом java.lang.Thread. В этом классе определены все методы, необходимые для создания потоков, управления их состоянием и синхронизации.
Как пользоваться классом Thread?
Есть две возможности.
- Во-первых, вы можете создать свой дочерний класс на базе класса Thread. При этом вы должны переопределить метод run. Ваша реализация этого метода будет работать в рамках отдельного потока.
- Во-вторых, ваш класс может реализовать интерфейс Runnable. При этом в рамках вашего класса необходимо определить метод run, который будет работать как отдельный поток.
Второй способ особенно удобен в тех случаях, когда ваш класс должен быть унаследован от какого-либо другого класса (например, от класса Applet) и при этом вам нужна многопоточность. Так как в языке программирования Java нет множественного наследования, невозможно создать класс, для которого в качестве родительского будут выступать классы Applet и Thread. В этом случае реализация интерфейса Runnable является единственным способом решения задачи.
Методы класса Thread
В классе Thread определены три поля, несколько конструкторов и большое количество методов, предназначенных для работы с потоками. Ниже мы привели краткое описание полей, конструкторов и методов.
С помощью конструкторов вы можете создавать потоки различными способами, указывая при необходимости для них имя и группу. Имя предназначено для идентификации потока и является необязательным атрибутом. Что же касается групп, то они предназначены для организации защиты потоков друг от друга в рамках одного приложения.
Методы класса Thread предоставляют все необходимые возможности для управления потоками, в том числе для их синхронизации.
Поля
Три статических поля предназначены для назначения приоритетов потокам.
Нормальный
public final static int NORM_PRIORITY;
Максимальный
public final static int MAX_PRIORITY;
Минимальный
public final static int MIN_PRIORITY;
Конструкторы
Создание нового объекта Thread
public Thread();
Создвание нового объекта Thread с указанием объекта, для которого будет вызываться метод run
public Thread(Runnable target);
Аналогично предыдущему, но дополнительно задается имя нового объекта Thread
public Thread(Runnable target, String name);
Создание объекта Thread с указанием его имени
public Thread(String name);
Создание нового объекта Thread с указанием группы потока и объекта, для которого вызывается метод run
public Thread(ThreadGroup group,
Runnable target);
Аналогично предыдущему, но дополнительно задается имя нового объекта Thread
public Thread(ThreadGroup group,
Runnable target, String name);
Создание нового объекта Thread с указанием группы потока и имени объекта
public Thread(ThreadGroup group, String name);
Методы
Текущее количество активных потоков в группе, к которой принадлежит поток
public static int activeCount();
Текущему потоку разрешается изменять объект Thread
public void checkAccesss();
Определение количества фреймов в стеке
public int countStackFrames();
Определение текущего работающего потока
public static Thread currentThread();
Принудительное завершение работы потока
public void destroy();
Вывод текущего содержимого стека для отладки
public static void dumpStack();
Получение всех объектов Tread данной группы
public static int enumerate(Thread tarray[]);
Определение имени потока
public final String getName();
Определение текущего приоритета потока
public final int getPriority();
Определение группы, к которой принадлежит поток
public final ThreadGroup getThreadGroup();
Прерывание потока
public void interrupt();
Определение, является ли поток прерванным
public static boolean interrupted();
Определение, выполняется поток или нет
public final boolean isAlive();
Определение, является ли поток демоном
public final boolean isDaemon();
Определение, является ли поток прерванным
public boolean isInterrupted();
Ожидание завершения потока
public final void join();
Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах
public final void join(long millis);
Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах и наносекундах
public final void join(long millis, int nanos);
Запуск временно приостановленного потока
public final void resume();
Метод вызывается в том случае, если поток был создан как объект с интерфейсом Runnable
public void run();
Установка для потока режима демона
public final void setDaemon(boolean on);
Устаовка имени потока
public final void setName(String name);
Установка приоритета потока
public final void setPriority(int newPriority);
Задержка потока на заднное время. Время задается в миллисекундах и наносекундах
public static void sleep(long millis);
Задержка потока на заднное время. Время задается в миллисекундах и наносекундах
public static void sleep(long millis, int nanos);
Запуск потока на выполнение
public void start();
Остановка выполнения потока
public final void stop();
Аварийная остановка выполнения потока с заданным исключением
public final void stop(Throwable obj);
Приостановка потока
public final void suspend();
Строка, представляющая объект-поток
public String toString();
Приостановка текущего потока для того чтобы управление было передано другому потоку
public static void yield ();
Создание дочернего класса на базе класса Thread
Рассмотрим первый способ реализации многопоточности, основанный на наследовании от класса Thread. При использовании этого способа вы определяете для потока отдельный класс, например, так:
class DrawRectangles extends Thread
{
. . .
public void run()
{
. . .
}
}
Здесь определен класс DrawRectangles, который является дочерним по отношению к классу Thread.
Обратите внимание на метод run. Создавая свой класс на базе класса Thread, вы должны всегда определять этот метод, который и будет выполняться в рамках отдельного потока.
Заметим, что метод run не вызывается напрямую никакими другими методами. Он получает управление при запуске потока методом start.
Как это происходит?
Рассмотрим процедуру запуска потока на примере некоторого класса DrawRectangles.
Вначале ваше приложение должно создать объект класса Thread:
public class MultiTask2 extends Applet
{
Thread m_DrawRectThread = null;
. . .
public void start()
{
if (m_DrawRectThread == null)
{
m_DrawRectThread = new DrawRectangles(this);
m_DrawRectThread.start();
}
}
}
Создание объекта выполняется оператором new в методе start, который получает управление, когда пользователь открывает документ HTML с аплетом. Сразу после создания поток запускается на выполнение, для чего вызывается метод start.
Что касается метода run, то если поток используется для выполнения какой либо периодической работы, то этот метод содержит внутри себя бесконечный цикл. Когда цикл завершается и метод run возвращает управление, поток прекращает свою работу нормальным, не аварийным образом. Для аварийного завершения потока можно использовать метод interrupt.
Остановка работающего потока выполняется методом stop. Обычно остановка всех работающих потоков, созданных аплетом, выполняется методом stop класса аплета:
public void stop()
{
if (m_DrawRectThread != null)
{
m_DrawRectThread.stop();
m_DrawRectThread = null;
}
}
Напомним, что этот метод вызывается, когда пользователь покидает страницу сервера Web, содержащую аплет.
Реализация интерфейса Runnable
Описанный выше способ создания потоков как объектов класса Thread или унаследованных от него классов кажется достаточнао естественным. Однако этот способ не единственный. Если вам нужно создать только один поток, работающую одновременно с кодом аплета, проще выбрать второй способ с использованием интерфейса Runnable.
Идея заключается в том, что основной класс аплета, который является дочерним по отношению к классу Applet, дополнительно реализует интерфейс Runnable, как это показано ниже:
public class MultiTask extends
Applet implements Runnable
{
Thread m_MultiTask = null;
. . .
public void run()
{
. . .
}
public void start()
{
if (m_MultiTask == null)
{
m_MultiTask = new Thread(this);
m_MultiTask.start();
}
}
public void stop()
{
if (m_MultiTask != null)
{
m_MultiTask.stop();
m_MultiTask = null;
}
}
}
Внутри класса необходимо определить метод run, который будет выполняться в рамках отдельного потока. При этом можно считать, что код аплета и код метода run работают одновременно как разные потоки.
Для создания потока используется оператор new. Поток создается как объект класса Thread, причем конструктору передается ссылка на класс аплета:
m_MultiTask = new Thread(this);
При этом, когда поток запустится, управление получит метод run, определенный в классе аплета.
Как запустить поток?
Запуск выполняется, как и раньше, методом start. Обычно поток запускается из метода start аплета, когда пользователь отображает страницу сервера Web, содержащую аплет. Остановка потока выполняется методом stop.