........................................................................................
}
и теперь необходимо, чтобы он выполнялся как отдельный поток. Для этого необходимо для этого класса реализовать интерфейс Runnable, создавая разделяемый метод run() интерфейса Runnable:
class myClass implements Runnable
{ // код класса - объявление его элементов и методов
........................................................................................
// этот метод получает управление при запуске потока
public void run()
{ // здесь можно добавить код, который будет
// выполняться в рамках отдельного потока
}
}
Рассмотрим пример и продолжим создание апплета MultiTask. Пусть существует класс, предназначенный для вывода случайной линии в окне апплета MultiTask:
class DrawLines
{ // конструктор, получающий ссылку на создателя объекта
public DrawLines(MultiTask parentObj)
{ parent=parentObj;
}
public void draw()
{ int w=parent. size().width-1, h=parent. size().height-1;
Graphics gr=parent. getGraphics();
gr. setColor(new Color((float)Math. random(),
(float)Math. random(),(float)Math. random()));
gr. drawLine((int)(Math. random()*w),(int)(Math. random()*h),
(int)(Math. random()*w),(int)(Math. random()*h));
}
MultiTask parent; // ссылка на создателя объекта
}
Реализуем для этого класса интерфейс Runnable и создадим метод run(), выполняющийся в рамках отдельного потока:
class DrawLines implements Runnable
{ // конструктор, получающий ссылку на создателя объекта
boolean going = 1;
public DrawLines(MultiTask parentObj)
{ parent=parentObj;
}
public void draw()
{ int w=parent. size().width-1, h=parent. size().height-1;
Graphics gr=parent. getGraphics();
gr. setColor(new Color((float)Math. random(),
(float)Math. random(),(float)Math. random()));
gr. drawLine((int)(Math. random()*w),(int)(Math. random()*h),
(int)(Math. random()*w),(int)(Math. random()*h));
}
MultiTask parent; // ссылка на создателя объекта
// этот метод получает управление при запуске потока
public void run()
{ while(going) { draw(); }
}
}
В классе апплета MultiTask объявляется элемент - объект класса Thread:
Thread Lines=null;
Затем в методе start() апплета создается поток Lines, при этом конструктору Thread() передается экземпляр класса DrawLines, реализующего интерфейс Runnable (создание потока с исполняемым адресатом). Далее поток Lines запускается на выполнение:
public void start()
{ if(Rects==null) { Rects=new DrawRects(this); Rects. start(); }
if(Ovals==null) { Ovals=new DrawOvals(this); Ovals. start(); }
if(Lines==null) { Lines=new Thread(new DrawLines(this)); Lines. start(); }
}
Все потоки апплета останавливаются в его методе stop():
public void stop()
{
if{ if(Rects!=null) Rects.going=0;
if(Ovals!=null) Ovals.going=0;
if(Lines!=null) Lines.going=0;
}
Применение анимации для мультизадачности
Одним из наиболее распространенных применений апплетов является создание анимационных эффектов типа бегущей строки, мерцающих огней и других эффектов, привлекающих внимание пользователя. Для достижения таких эффектов необходим механизм, позволяющий выполнять перерисовку всего окна апплета или его части периодически с заданным интервалом.
Работа апплетов основана на обработке событий. Основной (первичный) класс апплета обрабатывает события, переопределяя те или иные методы суперкласса Applet.
Проблема с периодическим обновлением окна апплета возникает из-за того, что в языке Java не предусмотрено никакого механизма для создания генератора событий, способного вызывать какой-то метод класса с заданным интервалом времени.
Перерисовка окна апплета выполняется методом paint(), который вызывается виртуальной машиной Java асинхронно по отношению к выполнению другого кода апплета, если содержимое окна было перекрыто другими окнами.
Возникает вопрос, а можно ли воспользоваться методом paint() для периодической перерисовки окна апплета, организовав в нем, например, бесконечный цикл с задержкой. К сожалению, так поступать нельзя. Метод paint() после перерисовки апплета должен сразу возвратить управление, иначе работа апплета будет заблокирована.
Единственный выход из создавшейся ситуации - создание потока (или нескольких потоков), которые будут выполнять рисование в окне апплета асинхронно по отношению к коду апплета. Например, можно создать поток, который периодически обновляет окно апплета, вызывая для этого метод repaint(), или рисовать из потока непосредственно в окне апплета (в апплете MultiTask использовался именно этот способ).
Если необходимо создать только одну задачу, работающую одновременно с кодом апплета, то проще выбрать способ реализации многозадачности с использованием интерфейса Runnable для подкласса класса Applet.
Идея заключается в том, что основной класс апплета DoubleTask (пример 2), который является дочерним по отношению к классу Applet, дополнительно реализует интерфейс Runnable, переопределяя метод run() этого интерфейса:
Пример 2. Файл DoubleTask. java -
import java. applet.*;
import java. awt.*;
public class DoubleTask extends Applet implements Runnable
{ public DoubleTask()
{ // здесь можно добавить код конструктора
}
public void init()
{ resize(320, 240); // установка размера апплета
// здесь можно добавить код инициализации апплета
}
public void destroy()
{ // здесь можно добавить код завершения работы апплета
}
public void paint(Graphics g)
{ // здесь можно добавить код вывода в окно апплета
}
public void start()
{ // создание и запуск потока
if(task==null) { task=new Thread(this); task. start(); }
}
public void stop()
{ // остановка потока
if(task!=null) task. stop();
}
public void run()
{ // здесь можно добавить код, который будет
// выполняться в рамках отдельного потока
}
Thread task=null; // ссылка на поток
// здесь можно добавить специализированный для класса код
}
Внутри класса необходимо определить метод run(), который будет выполняться в рамках отдельного потока. При этом можно считать, что код апплета и код метода run() работают одновременно как разные задачи.
Для создание потока используется оператор new. Поток создается как объект класса Thread, причем конструктору передается ссылка на класс апплета:
task=new Thread(this);
Запуск потока осуществляется методом start для объекта task. При запуске потока начинает работать метод run(), определенный в классе апплета. Причем одновременно с этим код методов апплета также выполняется, так что апплет может обрабатывать различные события в методах обработки событий.
Определим, например, метод paint() апплета следующим образом:
public void paint(Graphics g)
{ g. drawString("Random value "+Math. random(),10,20);
}
А в методе run() реализуем бесконечный цикл, в котором периодически с задержкой 50 миллисекунд вызывается метод repaint(), вызывающий перерисовку апплета:
public void run()
{ while(true)
{
repaint();
try{ Thread. sleep(50); } catch(InterruptedException e) { stop(); }
}
}
Для выполнения задержки метод run() вызывает sleep() класса Thread. Этот метод может вызвать исключение InterruptedException, которое можно обработать с помощью операторов try и catch. В данном случае при возникновении исключения апплет прекращает свою работу.
Такая перерисовка может вызвать мигание окна апплета, поэтому удачной ее назвать нельзя, но этот апплет можно использовать просто как пример реализации “двухзадачности” для апплета.
Апплет двойного назначения, реализующий интерфейс Runnable
Рассмотрим шаблон апплета двойного назначения, реализующего интерфейс Runnable. Данный пример во многом похож на пример 2, но новом апплете не только демонстрируется периодическое обновление окна с использованием многозадачности (одновременно с работой апплета выполняется еще одна задача), но и поддерживается возможность работы программы как независимого приложения.
Рассмотрим тексты создаваемых файлов.
Пример 3. Апплет с использованием многозадачности
//*************************************************************
// Multi. java: Applet
//
//*************************************************************
import java. applet.*;
import java. awt.*;
import MultiFrame;
public class Multi extends Applet implements Runnable
{
// Поток, который будет работать одновременно с апплетом
private Thread m_Multi = null;
boolean going = 1;
// Признак режима работы программы:
// true/false - приложение/апплет
//--------------------------------------------------------------------------
private boolean m_fStandAlone = false;
//--------------------------------------------------------------------------
public static void main(String args[]
{ // Создать рамку (фрейм) для апплета
MultiFrame frame = new MultiFrame("Title");
// До изменения размеров фрейма отобразить его.
// Это необходимо для того, чтобы метод insert()
// выдавал правильные значения
frame. show(); frame. hide();
frame. resize(frame. insets().left + frame. insets().right + 320,
frame. insets().top + frame. insets().bottom + 240);
// Создание объекта апплета, связывание апплета и фрейма
Multi applet_Combi = new Multi();
frame. add("Center", applet_Combi);
// Установление признака режима работы - приложение
applet_Combi. m_fStandAlone = true;
// Вызов методов ааплета для его запуска
applet_Combi. init();
applet_Combi. start();
// Отображение окна фрейма
frame. show();
}
//--------------------------------------------------------------------------
public Multi()
{ // Сделать: Добавьте сюда код конструктора
}
//--------------------------------------------------------------------------
public String getAppletInfo()
{ return "Name: CombiApplet\r\n" +
"";
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 |


