Реализация многопоточности в Java

Оглавление Многопоточность Процессы, потоки и приоритеты Многопоточность в Java Многопоточность и анимация Синхронизация потоков Потоки-демоны Аплет Rectangles Исходные тексты Описание текстов

Назад Вперед
должны воспользоваться классом java.lang.Thread. В этом классе определены все методы, необходимые для создания потоков, управления их состоянием и синхронизации.

Как пользоваться классом Thread?

Есть две возможности.

- Во-первых, вы можете создать свой дочерний класс на базе класса Thread. При этом вы должны переопределить метод run. Ваша реализация этого метода будет работать в рамках отдельного потока.
- Во-вторых, ваш класс может реализовать интерфейс Runnable. При этом в рамках вашего класса необходимо определить метод run, который будет работать как отдельный поток.

Второй способ особенно удобен в тех случаях, когда ваш класс должен быть унаследован от какого-либо другого класса (например, от класса Applet) и при этом вам нужна многопоточность. Так как в языке программирования Java нет множественного наследования, невозможно создать класс, для которого в качестве родительского будут выступать классы Applet и Thread. В этом случае реализация интерфейса Runnable является единственным способом решения задачи.

Методы класса Thread

В классе Thread определены три поля, несколько конструкторов и большое количество методов, предназначенных для работы с потоками. Ниже мы привели краткое описание полей, конструкторов и методов.

С помощью конструкторов вы можете создавать потоки различными способами, указывая при необходимости для них имя и группу. Имя предназначено для идентификации потока и является необязательным атрибутом. Что же касается групп, то они предназначены для организации защиты потоков друг от друга в рамках одного приложения.

Методы класса Thread предоставляют все необходимые возможности для управления потоками, в том числе для их синхронизации.

Поля

Три статических поля предназначены для назначения приоритетов потокам.

- NORM_PRIORITY

Нормальный

    public final static int NORM_PRIORITY;

- MAX_PRIORITY

Максимальный

    public final static int MAX_PRIORITY;

- MIN_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);

Методы

- activeCount

Текущее количество активных потоков в группе, к которой принадлежит поток

    public static int activeCount();

- checkAccess

Текущему потоку разрешается изменять объект Thread

    public void checkAccesss();

- countStackFrames

Определение количества фреймов в стеке

    public int countStackFrames();

- currentThread

Определение текущего работающего потока

    public static Thread currentThread();

- destroy

Принудительное завершение работы потока

    public void destroy();

- dumpStack

Вывод текущего содержимого стека для отладки

    public static void dumpStack();

- enumerate

Получение всех объектов Tread данной группы

    public static int enumerate(Thread  tarray[]);

- getName

Определение имени потока

    public final String getName();

- getPriority

Определение текущего приоритета потока

    public final int getPriority();

- getThreadGroup

Определение группы, к которой принадлежит поток

    public final ThreadGroup getThreadGroup();

- interrupt

Прерывание потока

    public void interrupt();

- interrupted

Определение, является ли поток прерванным

    public static boolean interrupted();

- isAlive

Определение, выполняется поток или нет

    public final boolean isAlive();

- isDaemon

Определение, является ли поток демоном

    public final boolean isDaemon();

- isInterrupted

Определение, является ли поток прерванным

    public boolean isInterrupted();

- join

Ожидание завершения потока

    public final void join();

Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах

    public final void join(long millis);

Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах и наносекундах

    public final void join(long  millis, int  nanos);

- resume

Запуск временно приостановленного потока

    public final void resume();

- run

Метод вызывается в том случае, если поток был создан как объект с интерфейсом Runnable

    public void run();

- setDaemon

Установка для потока режима демона

    public final void setDaemon(boolean on);

- setName

Устаовка имени потока

    public final void setName(String name);

- setPriority

Установка приоритета потока

    public final void setPriority(int newPriority);

- sleep

Задержка потока на заднное время. Время задается в миллисекундах и наносекундах

    public static void sleep(long millis);

Задержка потока на заднное время. Время задается в миллисекундах и наносекундах

    public static void sleep(long millis, int nanos);

- start

Запуск потока на выполнение

    public void start();

- stop

Остановка выполнения потока

    public final void stop();

Аварийная остановка выполнения потока с заданным исключением

    public final void stop(Throwable obj);

- suspend

Приостановка потока

    public final void suspend();

- toString

Строка, представляющая объект-поток

    public String toString();

- yield

Приостановка текущего потока для того чтобы управление было передано другому потоку

    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.

Назад Вперед