Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Microsoft Visual J++. Создание приложений и аплетов на языке Java. Часть 2

© Александр Фролов, Григорий Фролов
Том 32, М.: Диалог-МИФИ, 1997, 288 стр.

[Назад] [Содеожание] [Дальше]

Реализация мультизадачности в Java

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

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

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

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

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

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

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

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


public class java.lang.Thread
  extends java.lang.Object
  implements java.lang.Runnable
{
  // -----------------------------------------------------
  // Поля
  // -----------------------------------------------------

  // Приоритеты задач
  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 checkAccess();

  // Определение количества фреймов в стеке
  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

Рассмотрим первый способ реализации мультизадачности, основанный на наследовании от класса 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.

[Назад] [Содеожание] [Дальше]