В этом приложении мы создаем на базе класса Thread
три класса. Первый из них предназначен для
создания потока рисования прямоугольников,
второй - для создания потока рисования
закрашенных эллипсов, а третий - для управления
потоком рисования эллипсов.
Что же касается основного класса аплета, то он
унаследован, как обычно, от класса Applet и не
реализует интерфейс Runnable:
public class Rectangles
extends Applet
{
. . .
}
Поля класса Rectangles
В классе Rectangles мы определили три поля с именами
m_DrawRectThread, m_DrawEllipseThread и m_NotifyTaskThread:
DrawRectangles m_DrawRectThread = null;
DrawEllipse m_DrawEllipseThread = null;
NotifyTask m_NotifyTaskThread = null
Эти поля являются ссылками на классы,
соответственно DrawRectangles, DrawEllipse и NotifyTask . Первый из
них создан для рисования прямоугольников, второй
- эллипсов, а третий - для управления потоком
рисования эллипсов.
Указанные поля инициализируются занчением null,
что соответствует неработающим или несозданным
задачам.
Метод start класса Rectangles
Этот метод последовательно создает три потока
и запускает их на выполнение:
if(m_DrawRectThread == null)
{
m_DrawRectThread =
new DrawRectangles(this);
m_DrawRectThread.start();
}
if(m_DrawEllipseThread == null)
{
m_DrawEllipseThread =
new DrawEllipse(this);
m_DrawEllipseThread.start();
}
if(m_NotifyTaskThread == null)
{
m_NotifyTaskThread =
new NotifyTask(m_DrawEllipseThread);
m_NotifyTaskThread.start();
}
В качестве параметра конструкторам классов
DrawRectangles и DrawEllipse мы передаем ссылку на аплет
Rectangles. Эта ссылка будет нужна для получения
контекста отображения и рисования
геометрических фигур.
Поток класса NotifyTask будет управлять работой
потока DrawEllipse, поэтому мы передаем его
конструктору ссылку на соответствующий объект
m_DrawEllipseThread.
Метод stop класса Rectangles
Когда пользователь покидает страницу сервера
Web с аплетом, метод stop класса Rectangles
последовательно останавливает gjnjrb рисования
прямоугольников и эллипсов, а также управляющий
поток:
if(m_DrawRectThread != null)
{
m_DrawRectThread.stop();
m_DrawRectThread = null;
}
if(m_DrawEllipseThread == null)
{
m_DrawEllipseThread.stop();
m_DrawEllipseThread = null;
}
if(m_NotifyTaskThread != null)
{
m_NotifyTaskThread.stop();
m_NotifyTaskThread = null;
}
Поля класса DrawRectangles
Класс DrawRectangles определен для потока рисования
прямоугольников:
class DrawRectangles extends Thread
{
. . .
}
В поле g класа хранится контекст отображения
окна аплета, а в поле dimAppWndDimension - размеры этого
окна:
Graphics g;
Dimension dimAppWndDimension;
Значения этих полей определяются
конструктором класса по ссылке на главный класс
аплета.
Конструктор класса DrawRectangles
В качестве параметра конструктору передается
ссылка на класс аплета. Конструктор использует
эту ссылку для получения и сохранения в полях
класса контекста отображения и размеров окна
аплета:
public DrawRectangles(Applet Appl)
{
g = Appl.getGraphics();
dimAppWndDimension = Appl.getSize();
}
Метод run класса DrawRectangles
Программный код метода run работает в рамках
отдельного потока. Он рисует в окне аплета
закрашенные прямоугольники. Прямоугольники
имеют случайные координаты, расположение и цвет.
Для того чтобы рисовать, необходимо получить
контекст отображения. Этот контекст был получен
конструктором класса DrawRectangles и может быть
использован методом run.
Вооружившись контекстом отображения и
размерами окна аплета, поток входит в
бесконечный цикл рисования прямоугольников.
В качестве генератора случайных чисел мы
используем метод random из класса Math, который при
каждом вызове возвращает новое случайное число
типа double, лежащее в диапазоне значений от 0.0 до 1.0.
Координаты по осям X и Y рисуемого
прямоугольника определяются простым умножением
случайного числа, полученного от метода random,
соответственно, на ширину и высоту окна аплета:
x = (int)(dimAppWndDimension.width
* Math.random());
y = (int)(dimAppWndDimension.height
* Math.random());
Аналогично определяются размеры
прямоугольника, однако чтобы прямоугольники не
были слишком крупными, мы делим полученные
значения на 2:
width = (int)(dimAppWndDimension.width
* Math.random()) / 2;
height = (int)(dimAppWndDimension.height
* Math.random()) / 2;
Так как случайное число имеет тип double, в обоих
случаях мы выполняем явное преобразование
результата вычислений к типу int.
Для случайного выбора цвета прямоугольника мы
вычисляем отдельные цветовые компоненты,
умножая значение, полученное от метода random, на
число 255:
rColor = (int)(255 * Math.random());
gColor = (int)(255 * Math.random());
bColor = (int)(255 * Math.random());
Полученные значения цветовых компонент
используются в конструкторе Color для получения
цвета. Этот цвет устанавливается в контексте
отображения методом setColor:
g.setColor(new Color(rColor,
gColor, bColor));
Теперь все готово для рисования
прямоугольника, которое мы выполняем при помощи
метода fillRect:
g.fillRect(x, y, width, height);
После рисования прямоугольника метод run
задерживает свою работу на 50 миллисекунд,
вызывая метод sleep:
try
{
Thread.sleep(50);
}
catch (InterruptedException e)
{
stop();
}
Для обработки исключения InterruptedException, которое
может возникнуть во время работы этого метода, мы
предусмотрели блок try - catch. При возникновении
указанного исключения работа потока
останавливается вызовом метода stop.
Метод run класса DrawEllipse
Класс DrawEllipse очень похож на только что
рассмотренный класс DrawRectangles. Отличие есть только
в финальном фрагменте метода run, который мы и
рассмотрим.
Вместо задержки на 50 миллисекунд метод run из
класса DrawEllipse переходит в состояние ожидания
извещения, вызывая метод wait:
try
{
this.wait();
}
catch (InterruptedException e)
{
}
Это извещение создается управляющим потоком
класса NotifyTask, к описанию которого мы переходим.
Поля класса NotifyTask
В классе NotifyTask мы определили одно поле STask
класса Thread. Это поле которое хранит ссылку на
поток, работой которого управляет данный класс:
class NotifyTask extends Thread
{
Thread STask;
. . .
}
Конструктор класса NotifyTask
Конструктор класса NotifyTask записывает в поле STask
ссылку на задачу рисования эллипсов:
public NotifyTask(Thread SynchroTask)
{
STask = SynchroTask;
}
Метод run класса NotifyTask
Метод run класса NotifyTask периодически
разблокирует поток рисования эллипсов, вызывая
для этого метод notify в цилке с задержкой 30
миллисекунд. Обращение к объекту STask, который
хранит ссылку на поток рисования эллипсов,
выполняется с использованием синхронизации:
public void run()
{
while (true)
{
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
}
synchronized(STask)
{
STask.notify();
}
}
} |