Вы находитесь на странице: 1из 26

ЛАБОРАТОРНАЯ РАБОТА №1

РАЗРАБОТКА АППЛЕТОВ С ИСПОЛЬЗОВАНИЕМ


МНОГОПОТОЧНОСТИ
Цель: разработка многопоточного апплета.

Апплеты

Java можно использовать, чтобы создавать два типа программ


- приложения и апплеты. Приложения - это самостоятельные Java-
программы, а программы, работающие под управлением других программ
(Web-броузеров), называются апплетами.
Java-апплет — прикладная программа, чаще всего написанная на языке
программирования Java в форме байт-кода. Java-апплеты выполняются в веб-
обозревателе с использованием виртуальной Java машины (JVM), или в Sun's
AppletViewer, автономном средстве для испытания апплетов.
Апплеты используются для предоставления интерактивных
возможностей веб-приложений, которые не могут быть предоставлены
HTML. Так как байт-код Java платформо-независим, то Java-апплеты могут
выполняться с помощью плагинов браузерами многих платформ, включая
Microsoft Windows, UNIX, Apple Mac OS и GNU/Linux.

Структура и создание апплета

Главный класс апплета расширяет класс java.applet.Applet или, если


создаётся Swing апплет, javax.swing.JApplet. Класс должен
переопределить методы создания пользовательского интерфейса внутри себя.
(Applet является потомком Panel, который, в свою очередь, является
потомком Container).

Пример создания простейшего апплета:


import java.applet.*;
import java.awt.*;

public class MyFirstApplet extends Applet


{
public void paint(Graphics g)
{
g.drawString("Это мой первый Java-апплет!", 15, 15);
}
}
Рис. 1 – скриншот работающего апплета

Импортирование пакетов java.awt.* необходимо в нашем примере, т.к. они


содержат класс Graphics. Из этого примера также видно, как наш класс
MyFirstApplet наследует свойства и методы класса Applet, базового класса
для создания апплетов. В тексте нашего примера мы переопределяем лишь
один метод наследуемого класса Applet — это метод Paint, которым мы
выводим строчку в окне апплета в определенной нами позиции окна.

Класс Applet

Для работы с апплетами предназначен класс Applet, который


определяет методы, представленные в табл. 1.1. Applet обеспечивает всю
необходимую поддержку для выполнения апплетов, такую как запуск и
остановка. Он также реализует методы, которые загружают и показывают
изображения, и методы, которые загружают и проигрывают аудио-клипы.
Таблица 1.1
Методы, определенные в классе Applet

Метод Описание
1 2
void destroy() Освобождает все ресурсы, занятые апплетом.
Вызывается браузером непосредственно перед тем,
как апплет завершается.
Метод destroy() вызывается, когда среда
решает, что апплет должен быть полностью удален из
памяти. В этот момент следует освободить любые ре-
сурсы, которые апплет может использовать.
String Возвращает параметр, указанный в paramName.
getParameter(Str Если указанный параметр не найден, возвращается
ing paramName) null (пустой указатель)
Окончание табл. 1.1
1 2
void init() Вызывается, когда апплет начинает выполнение. Это
первый метод, который вызывается для любого
апплета. В нем необходимо инициализировать
переменные. Вызывается этот метод один раз в
течение времени выполнения апплета.
boolean Возвращает true, если апплет был запущен.
isActive() Возвращает false, если апплет был остановлен

void Изменяет размеры апплета согласно


resize(Dimension измерениям, указанным в dim
dim)
void start() Вызывается, чтобы перезапустить апплет после
его остановки. В то время как init() вызывается
один раз (когда апплет загружается), start()
запускается каждый раз, когда HTML-документ
апплета отображается на экране. Так, если поль-
зователь покидает Web-страницу и возвращается
обратно, апплет возобновляет выполнение в start().
void stop() Метод stop() вызывается, если Web-браузер
покидает HTML-документ, содержащий апплет, при
переходе к другой странице. Когда вызывается
stop(), апплет, вероятно, продолжает выполняться.
Следует использовать stop() для приостановки
потоков, не требующих выполнения, если апплет
невидим. Их можно перезапустить вызывом start(),
когда пользователь возвращается к странице.
Метод stop() всегда вызывается перед
destroy().

Важно понять порядок, в котором вызываются различные методы


апплета. Когда апплет начинает выполняться, AWT вызывает методы в такой
последовательности:
1) init(); 2) start(); 3) paint();
При завершении апплета имеет место следующая последовательность
вызовов:
1) stop(); 2) destroy();
Рисование в апплете

Рисование линий в апплете


Линии рисуются методом drawLine() формата:
void drawLine (int startx, int startY, int endX, int
endY)
Данный метод отображает линию (в текущем цвете рисования), которая
начинается в координатах startX, startY и заканчивается в endX, endY.
Пример использования метода:

Пример:
public void paint(Graphics g) {
g.drawLine(0, 0, 100, 100);
}

Рисование прямоугольников в апплете


Методы drawRect() и fillRect() отображают соответственно
рисованный и заполненный прямоугольник. Их форматы:

void drawRect(int top, int left, int width,


int height)
void fillRect(int tap, int left, int width,
int height)

Координаты левого верхнего угла прямоугольника задаются в


параметрах top и left, width и height, указывающих размеры
прямоугольника (в пикселах).
Пример:
public void paint(Graphics g)
{ g.drawRect(10, 10, 60, 50);
g.fillRect(100, 10, 60, 50);
}

Рисование эллипсов и кругов в апплете


Для рисования эллипса используется drawOval (), а для его
заполнения – fillOval(). Эти методы имеют форматы:

void drawOval(int top, int left, int width, int


height)
void fillOval(int top, int left, int width,
int height)
Пример :
public void paint(Graphics g)
{ g.drawOval(10, 10, 50, 50);
g.fillOval(100, 10, 75, 50);
}
Рисование дуг в апплете
Дуги можно рисовать методами drawArc() и fillArct(), используя
форматы:

void drawArc(int top, int left, int width,


int height, int начало, int конец)
void fillArc(int top, int left, int width, int
height, int начало, int конец)

Дуга ограничена прямоугольником; левый верхний угол


прямоугольника определяется параметрами top, left, а ширина и высота
— параметрами width и height. Дуга рисуется от начала до углового
расстояния, указанного в конец. Углы указываются в градусах и
отсчитываются от горизонтальной оси против часовой стрелки. Дуга
рисуется против часовой стрелки, если конец положителен, и по часовой
стрелке, если конец отрицателен. Поэтому, чтобы нарисовать дугу от 12-
часового до 6-часового положений, начальный угол должен быть 90° и угол
развертки 180°.

Пример:
public void paint(Graphics g) {
g.drawArc(0, 40, 70, 70, 0, 75);
g. fillArc (0, 40, 70, 70, 0, 75);
}

Работа с цветом
Работа с цветом поддерживается классом Color. В Color определено
несколько цветовых констант (например color.black), специфицирующих
ряд обычных цветов. Возможно также создание собственных цветов,
применением одного из цветовых конструкторов. Обычно используются
следующие его форматы:

Color (int red, int green, int blue)


Color (int rgbValue)
Color(float red, float green, float blue)
Пример:
new Color(255, 100, 100); // светло-красный

Потоки

Когда запускается любое приложение, то начинает выполняться поток,


называемый главным потоком (main). От него порождаются дочерние
потоки. Главный поток, как правило, является последним потоком,
завершающим выполнение программы.
Несмотря на то, что главный поток создаётся автоматически, им можно
управлять через объект класса Thread. Для этого нужно вызвать
метод currentThread(), после чего можно управлять потоком.
Класс Thread содержит несколько методов для управления потоками.

 getName() - получить имя потока


 getPriority() - получить приоритет потока
 isAlive() - определить, выполняется ли поток
 join() - ожидать завершение потока
 run() - запуск потока
 sleep() - приостановить поток на заданное время
 start() - запустить поток вызовом метода start()

Многозадачность

Многозадачность, основанная на потоках как минимальной единице


диспетчеризации называется многопоточностью, т.е. многопоточность – это
реализация процесса на основе параллельно выполняющихся потоков.
В отличие от многозадачности, где задачи используют собственное
адресное пространство, потоки используют одно и то же адресное
пространство и разделяют процессорное время. В многопоточном режиме
блокировка одного потока не приводит к остановке всей программы,
например, в ожидании занятого ресурса, а вызывает передачу управления
потоку, который может выполняться. Java обеспечивает встроенную
поддержку для многопоточного программирования. В Java назначается
приоритет, определяющий порядок обработки одного потока относительно
другого, т.е. порядок переключения процессора от выполняющегося потока к
следующему (переключение контекста). Абсолютное значение приоритета
потока само по себе не имеет значения.
Правила переключения:

 добровольный отказ от управления (кооперация) – явный переход в


режим ожидания или блокированием на ожидании ввода/вывода и
передача управления самому высокоприоритетному потоку, готовому к
выполнению;
 упреждающая многозадачность – поток может быть приостановлен
более приоритетным потоком. В этом случае выполнение
низкоприоритетного потока приостанавливается, независимо от того,
какие действия он выполняет, более высокоприоритетным потоком
Распределение процессора для потоков с одинаковым приоритетом
может быть реализовано, например, квантованием времени использования
процессора, добровольной передачи управления, либо другими более
сложными стратегиями.
Т.к. многопоточность предполагает асинхронное выполнение потоков,
то в каких-то точках может понадобиться синхронизация потоков. Для этого
используется «монитор», называемый так же семафором – механизм
управления связью между процессами – это блок, который может
использоваться только одним потоком, а следовательно, монитор можно
использовать для разделения ресурса между несколькими потоками. В Java
нет отдельного класса объектов «монитор». Объект потока располагает
собственным неявным монитором, который вводится автоматически, как
только поток управления начинает выполнять синхронизированный метод,
т.е. никакой другой поток не может вызвать данный метод до его
освобождения.
Взаимодействия потоков в Java реализуются через вызовы
предопределенных методов, которыми обладают все объекты.
Один из потоков – «главный» начинает выполняться первым при
запуске Java-программы. От него порождаются дочерние потоки. Главный
поток является последним выполняющимся потоком. Программа
завершается, когда главный поток останавливается.

Создание нового потока

Поток может быть создан на основе базового класса Thread или


реализации интерфейса Runnable. Класс Thread инкапсулирует ряд
методов, которые помогают управлять потоками.
java.lang.Object
java.lang.Thread
Главный поток создается автоматически с именем main и приоритетом
5 по умолчанию. Им можно управлять через Thread объект. Ссылку на него
можно получить через статический метод static currentThread ( ), т.е.
выполнить, например:
Thread tp = Thread.CurrentThread ( ) – возвращает ссылку на
поток, в котором он вызывается. В дальнейшем эту объектную ссылку
можно использовать для вызова методов в управлении потоками.
Например, установить новое имя потока при помощи (string).
Thread tp = Thread.CurrentThread ( )
System.ont.println («новое имя потока:» +
tp.setName («New name»);.
Новое имя потока: Thread [New name, 5,main], где NewName – новое
имя потока; 5- приоритет, заданный по умолчанию.
main –имя группы потоков, которой принадлежит данный поток.
Группа потоков – структура данных, контролирующая состояние
совокупности потоков в целом.
Самый простой способ создания – реализация интерфейса Runnable, который
требует определения в классе реализации метода: public void run ( ); ,
устанавливающего в программе точку входа для запуска нового
конкурирующего потока выполнения.

Примеры создания потоков

 анонимный класс
// Создание потока
Thread t = new Thread(new Runnable() {
public void run() {
System.out.println("Hello");
}
});
// Запуск потока
t.start();

 класс, реализующий Runnable и метод run()


public class myTask
implements Runnable {
public void run() {
// действия
}
}
// Создание потока
Thread t = new Thread(new myTask());
// Запуск потока
t.start();

 Класс, наследующийся от Thread и реализующий метод run()


public class myTask
extends Thread {
public void run() {
// действия
}
}
// Создание потока
Thread t = new myTask();
// Запуск потока
t.start();
В данном случае также можно вызвать start() в конструкторе

Синхронизация

Возможность синхронизации как бы встроена в каждый объект, создаваемый


приложением Java. Для этого объекты снабжаются защелками, которые
могут быть использованы для блокировки потоков, обращающихся к этим
объектам.

Чтобы воспользоваться защелками, вы можете объявить соответствующий


метод как synchronized, сделав его синхронизированным:

public synchronized void decrement()


{
}

При вызове синхронизированного метода соответствующий ему объект (в


котором он определен) блокируется для использования другими
синхронизированными методами. В результате предотвращается
одновременная запись двумя методами значений в область памяти,
принадлежащую данному объекту.

Использование синхронизированных методов - достаточно простой способ


синхронизации потоков, обращающихся к общим критическим ресурсам,
наподобие описанного выше банковского счета.

Заметим, что не обязательно синхронизовать весь метод - можно выполнить


синхронизацию только критичного фрагмента кода.

synchronized(Account)
{
if(Account.check(3000000))
Account.decrement(3000000);
}
Здесь синхронизация выполняется для объекта Account.

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

Блокировка на заданный период времени

С помощью метода sleep можно заблокировать поток на заданный период


времени:

try
{
Thread.sleep(500);
}
catch (InterruptedException ee)
{
}

В данном примере работа потока  Thread приостанавливается на 500


миллисекунд. Заметим, что во время ожидания приостановленный поток не
отнимает ресурсы процессора.

Так как метод sleep может создавать исключение


InterruptedException, необходимо предусмотреть его обработку. Для
этого мы использовали операторы try и catch.

Временная приостановка и возобновление работы

Методы suspend и resume позволяют, соответственно, временно


приостанавливать и возобновлять работу потока.

В следующем фрагменте кода поток m_Rectangles приостанавливает свою


работу, когда курсор мыши оказывается над окном аплета:

public boolean mouseEnter(Event evt,


int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.suspend();
}
return true;
}
Работа потока возобновляется, когда курсор мыши покидает окно аплета:

public boolean mouseExit(Event evt,


int x, int y)
{
if (m_Rectangles != null)
{
m_Rectangles.resume();
}
return true;
}
Ожидание извещения

Если вам нужно организовать взаимодействие потоков таким образом, чтобы


один поток управлял работой другого или других потоков, вы можете
воспользоваться методами wait, notify и notifyAll, определенными в
классе Object.

Метод wait может использоваться либо с параметром, либо без параметра.


Этот метод переводит поток в состояние ожидания, в котором он будет
находиться до тех пор, пока для потока не будет вызван извещающий метод
notify, notifyAll, или пока не истечет период времени, указанный в
параметре метода wait.

Как пользоваться методами wait, notify и notifyAll?

Метод, который будет переводиться в состояние ожидания, должен быть


синхронизированным, то есть его следует описать как synchronized:

public synchronized void run()


{
while (true)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
}
}
}

В этом примере внутри метода run определен цикл, вызывающий метод wait
без параметров. Каждый раз при очередном проходе цикла метод run
переводится в состояние ожидания до тех пор, пока другой поток не
выполнит извещение с помощью метода notify.

Ниже мы привели пример потока, вызывающией метод notify:

public void run()


{
while (true)
{
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
}

synchronized(STask)
{
STask.notify();
}
}
}

Этот поток реализован в рамках отдельного класса, конструктору которого


передается ссылка на поток, вызывающую метод wait. Эта ссылка хранится в
поле STask.

Обратите внимание, что хотя сам метод run не синхронизированный, вызов


метода notify выполняется в синхронизированном режиме. В качестве
объекта синхронизации выступает поток, для которого вызывается метод
notify.

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

С помощью метода join вы можете выполнять ожидание завершения работы


потока, для которой этот метод вызван.

Существует три определения метода join:

public final void join();


public final void join(long millis);
public final void join(long millis,
int nanos);
Первый из них выполняет ожидание без ограничения во времени, для
второго ожидание будет прервано принудительно через millis
миллисекунд, а для третьего - через millis миллисекунд и nanos
наносекунд. Учтите, что реально вы не сможете указывать время с точностью
до наносекунд, так как дискретность системного таймера компьютера
намного больше.

Примеры разработки апплетов с использованием многопоточности

Пример 1.

Разработать апплет с 5 (минимум) потоками, содержащий схематическое


изображение движущегося велосипеда. Предусмотреть возможность задавать
движение нажатием кнопки.

import java.awt.*;
import java.applet.*;
import java.lang.Math.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.logging.Level;
import java.util.logging.Logger;
//создать класс апплета, который реализует интерфейс
Runnable

public class AppletThreadSample extends Applet


implements Runnable{
private Thread T; //создать объект потока
//объявление переменных
private ShapeRect m_ShapeRect = null; //для педальки
private ShapeKoleso_1 m_ShapeKoleso_1 = null; //для 1
спицы 1 колеса
private ShapeKoleso_2 m_ShapeKoleso_2 = null; //для 2
спицы 1 колеса
private ShapeKoleso_3 m_ShapeKoleso_3 = null; //для 3
спицы 1 колеса
private ShapeKoleso_4 m_ShapeKoleso_4 = null; //для 1
спицы 2 колеса
private ShapeKoleso_5 m_ShapeKoleso_5 = null; //для 2
спицы 2 колеса
private ShapeKoleso_6 m_ShapeKoleso_6 = null; //для 3
спицы 2 колеса
public void run() { //реализация метода run, точка
входа в поток
setBackground(Color.yellow); //фон апплета
зарисовывается желтым
while (true){ //бесконечный цикл
repaint(); //перерисовка апплета или вызов метода paint
try{
T.sleep(10); //приостановка апплета на 10 миллисекунл
}
catch (InterruptedException e){ }
}
}
public void init() { //метод инициализации апплета
Font font=new
Font("TimesRoman",Font.PLAIN,24);
setFont(font);
Button button=new Button("Первая кнопка");
add(button);
T = new Thread(this); //создание потока и привязка его
к текущему клас-су

m_ShapeKoleso_1 = new ShapeKoleso_1();


m_ShapeKoleso_2 = new ShapeKoleso_2();
m_ShapeKoleso_3 = new ShapeKoleso_3();
m_ShapeKoleso_4 = new ShapeKoleso_4();
m_ShapeKoleso_5 = new ShapeKoleso_5();
m_ShapeKoleso_6 = new ShapeKoleso_6();
m_ShapeRect= new ShapeRect();
}
public void paint(Graphics g) { //метод прорисовки
апплета
//прорисовка строки
g.drawLine(272, 449, 305, 369);
g.drawLine(305, 369, 446, 352);
g.drawLine(446, 352, 361, 437);
g.drawLine(361, 437, 280, 456);
g.drawLine(361, 437, 280, 337);
g.drawLine(261, 343, 305, 327);
g.drawLine(305, 327, 255, 322);
g.drawLine(255, 322, 261, 343);
g.drawOval(265, 450, 15, 15);
g.drawOval(212, 400, 115, 110);
g.drawLine(478, 448, 435, 315);
g.drawLine(435, 315, 460, 299);
g.drawLine(460, 299, 468, 314);
g.drawOval(471, 450, 15, 15);
g.drawOval(416, 400, 115, 110);
g.drawOval(348, 429, 40, 40);
//прорисовка квадрата
g.setColor(Color.red);
g.drawLine(m_ShapeRect.x_Rect,m_ShapeRect.y_Rect,m_Shap
eRect.w_Rect,m_ShapeRect.h_Rect); //ступенька педальки
g.drawLine(m_ShapeRect.w_Rect, m_ShapeRect.h_Rect,
m_ShapeRect.w_Rect+15, m_ShapeRect.h_Rect);//сама
педалька
g.drawLine(m_ShapeKoleso_1.x_Koleso_1,
m_ShapeKoleso_1.y_Koleso_1, m_ShapeKoleso_1.w_Koleso_1,
m_ShapeKoleso_1.h_Koleso_1); // спица 1 колеса 1
g.drawLine(m_ShapeKoleso_2.x_Koleso_2,
m_ShapeKoleso_2.y_Koleso_2, m_ShapeKoleso_2.w_Koleso_2,
m_ShapeKoleso_2.h_Koleso_2); // спица 2 колеса 1
g.drawLine(m_ShapeKoleso_3.x_Koleso_3,
m_ShapeKoleso_3.y_Koleso_3, m_ShapeKoleso_3.w_Koleso_3,
m_ShapeKoleso_3.h_Koleso_3); // спица 3 колеса 1
g.drawLine(m_ShapeKoleso_4.x_Koleso_4,
m_ShapeKoleso_4.y_Koleso_4, m_ShapeKoleso_4.w_Koleso_4,
m_ShapeKoleso_4.h_Koleso_4); // спица 1 колеса 1
g.drawLine(m_ShapeKoleso_5.x_Koleso_5,
m_ShapeKoleso_5.y_Koleso_5, m_ShapeKoleso_5.w_Koleso_5,
m_ShapeKoleso_5.h_Koleso_5); // спица 2 колеса 1
g.drawLine(m_ShapeKoleso_6.x_Koleso_6,
m_ShapeKoleso_6.y_Koleso_6, m_ShapeKoleso_6.w_Koleso_6,
m_ShapeKoleso_6.h_Koleso_6); // спица 3 колеса 1

g.setColor(Color.blue);
g.fillOval(265, 450, 15, 15);
g.setColor(Color.black);

g.setColor(Color.blue);
g.fillOval(471, 450, 15, 15);
g.setColor(Color.black);

}
//класс ShapeRect реализующий интерфейс Runnable
class ShapeKoleso_1 implements Runnable{
Thread T;
double angle_a;
int x_Koleso_1,y_Koleso_1,w_Koleso_1,h_Koleso_1;
//координаты и размеры спицы
public ShapeKoleso_1(){ //конструктор
T = new Thread(this); //создание объекта Thread
//установление начальных координат спицы
angle_a=0.175;
x_Koleso_1=272;y_Koleso_1=456;w_Koleso_1=290;h_Koleso_1
=397;
T.start();//запуск потока (вызов метода run)
}
public void run(){ //метод run
for(;;){
angle_a+=0.175;
w_Koleso_1= (int) (x_Koleso_1 - 57 *
Math.cos(angle_a)) ; //изменение координаты спицы
h_Koleso_1= (int) (y_Koleso_1 - 57 *
Math.sin(angle_a));
try{
T.sleep(150); //приостановка работы потока на 150
миллисекунд
}
catch (InterruptedException e){}
}
}
}
class ShapeRect implements Runnable{
Thread T;
double angle_a;
int x_Rect,y_Rect,w_Rect,h_Rect; //координаты и размеры
педальки
public ShapeRect(){ //конструктор
T = new Thread(this); //создание объекта Thread
//установление начальных координат педальки
angle_a=0.175;
x_Rect=369;y_Rect=451;w_Rect=357;h_Rect=483;
T.start();//запуск потока (вызов метода run)
}
public void run(){ //метод run
for(;;){
angle_a+=0.175;
w_Rect= (int) (x_Rect - 34 * Math.cos(angle_a))
; //изменение координаты педальки
h_Rect= (int) (y_Rect - 34 *
Math.sin(angle_a));
try{
T.sleep(150); //приостановка работы потока на 150
миллисекунд
}
catch (InterruptedException e){}
}
}
}

class ShapeKoleso_2 implements Runnable{


Thread T;
double angle_a;
int x_Koleso_2,y_Koleso_2,w_Koleso_2,h_Koleso_2;
//координаты и размеры спицы
public ShapeKoleso_2(){ //конструктор
T = new Thread(this); //создание объекта Thread
//установление начальных координат спицы
angle_a=2.0;
x_Koleso_2=272;y_Koleso_2=456;
T.start();//запуск потока (вызов метода run)
}
public void run(){ //метод run
for(;;){
angle_a+=0.175;
w_Koleso_2= (int) (x_Koleso_2 - 57 *
Math.cos(angle_a)) ; //изменение координаты спицы
h_Koleso_2= (int) (y_Koleso_2 - 57 *
Math.sin(angle_a));
try{
T.sleep(150); //приостановка работы потока на 150
миллисекунд
}
catch (InterruptedException e){}
}
}
}
class ShapeKoleso_3 implements Runnable{
Thread T;
double angle_a;
int x_Koleso_3,y_Koleso_3,w_Koleso_3,h_Koleso_3;
//координаты и размеры спицы
public ShapeKoleso_3(){ //конструктор
T = new Thread(this); //создание объекта Thread
//установление начальных координат спицы
angle_a=4.189;
x_Koleso_3=272;y_Koleso_3=456;
T.start();//запуск потока (вызов метода run)
}
public void run(){ //метод run
for(;;){
angle_a+=0.175;
w_Koleso_3= (int) (x_Koleso_3 - 57 *
Math.cos(angle_a)) ; //изменение координаты спицы
h_Koleso_3= (int) (y_Koleso_3 - 57 *
Math.sin(angle_a));
try{
T.sleep(150); //приостановка работы потока на 150
миллисекунд
}
catch (InterruptedException e){}
}
}
}
class ShapeKoleso_4 implements Runnable{
Thread T;
double angle_a;
int x_Koleso_4,y_Koleso_4,w_Koleso_4,h_Koleso_4;
//координаты и размеры спицы
public ShapeKoleso_4(){ //конструктор
T = new Thread(this); //создание объекта Thread
//установление начальных координат спицы
angle_a=0.175;
x_Koleso_4=477;y_Koleso_4=456;
T.start();//запуск потока (вызов метода run)
}
public void run(){ //метод run
for(;;){
angle_a+=0.175;
w_Koleso_4= (int) (x_Koleso_4 - 57 *
Math.cos(angle_a)) ; //изменение координаты спицы
h_Koleso_4= (int) (y_Koleso_4 - 57 *
Math.sin(angle_a));
try{
T.sleep(150); //приостановка работы потока на 150
миллисекунд
}
catch (InterruptedException e){}
}
}
}
class ShapeKoleso_5 implements Runnable{
Thread T;
double angle_a;
int x_Koleso_5,y_Koleso_5,w_Koleso_5,h_Koleso_5;
//координаты и размеры спицы
public ShapeKoleso_5(){ //конструктор
T = new Thread(this); //создание объекта Thread
//установление начальных координат спицы
angle_a=2.0;
x_Koleso_5=477;y_Koleso_5=456;
T.start();//запуск потока (вызов метода run)
}
public void run(){ //метод run
for(;;){
angle_a+=0.175;
w_Koleso_5= (int) (x_Koleso_5 - 57 *
Math.cos(angle_a)) ; //изменение координаты спицы
h_Koleso_5= (int) (y_Koleso_5 - 57 *
Math.sin(angle_a));
try{
T.sleep(150); //приостановка работы потока на 150
миллисекунд
}
catch (InterruptedException e){}
}
}
}
class ShapeKoleso_6 implements Runnable{
Thread T;
double angle_a;
int x_Koleso_6,y_Koleso_6,w_Koleso_6,h_Koleso_6;
//координаты и размеры спицы
public ShapeKoleso_6(){ //конструктор
T = new Thread(this); //создание объекта Thread
//установление начальных координат спицы
angle_a=4.189;
x_Koleso_6=477;y_Koleso_6=456;
T.start();//запуск потока (вызов метода run)
}
public void run(){ //метод run
for(;;){
angle_a+=0.175;
w_Koleso_6= (int) (x_Koleso_6 - 57 *
Math.cos(angle_a)) ; //изменение координаты спицы
h_Koleso_6= (int) (y_Koleso_6 - 57 *
Math.sin(angle_a));
try{
T.sleep(150); //приостановка работы потока на 150
миллисекунд
}
catch (InterruptedException e){}
}
}
}
public boolean action(Event evt, Object arg) {
if(arg=="Начать движение велосипеда")
repaint();
return true;
}

Рис. 2 – пример выполнения апплета

Пример 2.
Разработать апплет с использованием многопоточности, изображающий
поэтапную схематическую прорисовку домика.
import java.applet.Applet;
import java.awt.*;

public class AppletThreadSample extends Applet


implements Runnable{
int level = 0;
Thread t;
//функция инициализации апплета
@Override
public void init()
{
this.setBackground(Color.white);
t = new Thread(this);
this.setSize(400, 400);
t.start();
}
//функция перерисовки апплета
public void paint(Graphics g)
{
g.setColor(Color.DARK_GRAY);
if(level == 1)
{
g.drawLine(50, 150, 200,
50);
g.drawLine(200, 50, 350,
150);
g.drawLine(350, 150, 50,
150);
}
if(level == 2)
{
g.drawLine(50, 150, 200,
50);
g.drawLine(200, 50, 350,
150);
g.drawLine(350, 150, 50,
150);
g.drawRect(100, 150, 200,
200);
}
if(level == 3)
{
g.drawLine(50, 150, 200,
50);
g.drawLine(200, 50, 350,
150);
g.drawLine(350, 150, 50,
150);
g.drawRect(100, 150, 200,
200);
g.drawRect(170, 200, 60,
100);
}
if(level == 4)
{
g.drawLine(50, 150, 200,
50);
g.drawLine(200, 50, 350,
150);
g.drawLine(350, 150, 50,
150);
g.drawRect(100, 150, 200,
200);
g.drawRect(170, 200, 60,
100);
g.drawLine(200, 200, 200,
300);
g.drawLine(170, 250, 230,
250);
g.setColor(Color.MAGENTA);
g.drawString("Домик", 190,
30);
}
}
//запуск потока
public void run()
{
System.out.println("Run");
while(true)
{
level ++;
repaint();
try{
Thread.currentThread().sleep(3000);
}
catch(Exception ex){}
if(level == 4)
{
return;
}
}
}
}

Рис. 3 – пример выполнения апплета

Задания для самостоятельного выполнения

Разработать апплет с 5 (минимум) потоками, выводящий на экран


изображение и предусмотреть возможность запуска/остановки движения в
апплете. Изображение на экране должно содержать:
1. Велосипедиста (схематичное). При запуске апплета велосипедист
начинает движение, вращая ногами педали велосипеда.
2. Человека (схематичное). При запуске апплета человек начинает идти,
размахивая в такт движения руками.
3. Облака в верхней части экрана. При запуске апплета облако начинает
двигаться и из него начинает идти дождь. При этом размер облака
постепенно уменьшается.
4. Тучи в верхней части экрана, а в нижнюю часть экрана – емкость для
воды. При запуске апплета начинает идти дождь. При этом размер тучи
уменьшается, a емкость наполняется водой.
5. Тучи в верхней части экрана, а в нижнюю часть экрана – сугроб. При
запуске апплета начинает идти снег. При этом размер тучи уменьшается, a
сугроб растет.
6. Летящего самолета.
7. Пушки. В правой части экрана появляется и исчезает (случайным
образом) мишень. Нажатием кнопки производится выстрел из пушки.
Момент попадания фиксируется в виде взрыва.
8. Движущегося слева направо парусника с постоянной скоростью. Ее
значение всякий раз задается генератором случайных чисел. В нижней части
экрана расположена пушка. При нажатии кнопки происходит выстрел
торпедой с постоянной скоростью. При попадании торпеды в пушку
смоделировать взрыв парусника и его исчезновение. При промахе парусник
достигает правой границы экрана и начинает движение сначала с новой
постоянной скоростью.
9. Лыжника. При нажатии кнопки он начинает движение
классическим стилем.
10. Человека в положении готовности осуществить прыжок в длину. При
нажатии кнопки спортсмен начинает разбег и выполняет такой прыжок.
11. Циферблата механических часов с секундной, минутной и часовой
стрелками. Организовать срабатывание будильника в заданное время.
12. Песочные часы. При нажатии кнопки моделируется процесс падения
песчинок, уменьшение уровня песка в верхней части колбы и увеличение в
нижней части колбы.
13. Треугольник. При нажатии клавиши курсор вправо треугольник
вращается по часовой стрелке, клавиши курсор влево − против часовой
стрелки.
14. Бабочки. При нажатии кнопки бабочка начинает полет, взмахивая
крылями.
15. Три вложенные друг в друга окружности, представляющие собой
беговые дорожки. На линию старта выходят три спортсмена (произвольные
фигуры). При нажатии кнопки участники стартуют с одинаковой угловой
скоростью. После старта угловые скорости участников забега изменяются по
случайному закону. На финише указать место, занятое каждым участником
забега.
16. Изображение домика с окном и трубой. При нажатии на кнопку в окне
загарается свет, а из трубы начинает идти дым.
17. Два автомобиля. После нажатия на кнопку они начинают двигаться,
при этом скорость их движения изменяется случайным образом. После
достижения финишной черты автомобили останвливаются.
18. Новогодние елки с тремя гирляндами. При нажатии на кнопку
гирлянды начинают переливаться разными огнями при этом каждая в своем
режиме.
19. Плавающую среди покачивающихся водорослей морскую рыбку.
20. Летящий вертолет.
21. Проезжающего автомобиля и человека, идущего по тротуару.
22. Циферблат механических часов с секундной, минутной и часовой
стрелками. Организовать срабатывание будильника в заданное время.
23. Три вложенные друг в друга окружности, представляющие собой
беговые дорожки. На линию старта выходят три спортсмена (произвольные
фигуры). При нажатии кнопки участники стартуют с одинаковой угловой
скоростью. После старта угловые скорости участников забега изменяются по
случайному закону. На финише указать место, занятое каждым участником
забега.
24. Домик с окном и трубой. При нажатии на кнопку в окне загарается
свет, а из трубы начинает идти дым.

Контрольные вопросы

1. Что такое принцип многопоточности?


2. Что такое поток?
3. Что такое класс Thread, какие методы он содержит?
4. Что такое интерфейс Runnablе, для чего он используется?
5. Что такое метод или блок кода с ключевым словом synchronized?
6. Что такое обоюдная блокировка?
7. Какие существуют правила переключения?
8. Что такое многозадачность?
9. Как можно приостанавливать выполнение потока?
10. Какие существуют основные методы для работы с потоками?

Список литературы:

1. www.studfiles.ru/preview/2854789/
2. ru.wiki2.org/wiki/Java-апплет
3. habrahabr.ru/post/164487/
4. wm-help.net/books-online/book/12462/12462-72.html
5. javalang.website/c14.html
6. libeldoc.bsuir.by/bitstream/123456789/549/1/Языки_...VA_Лаб_практ_Ч_
2.pdf
7. javac.at.ua/news/mnogopotochnost_java_ochen_ponjat...statja/2011-10-
24-14
8. www.studfiles.ru/preview/834380/
9. studopedia.org/8-213606.html
10. ru.wikipedia.org/wiki/Java-апплет
11. studopedia.net/14_60251_paket-AWT.html
12. studopedia.net/14_60254_mnogopotochnoe-programmirovanie.html
13. lomos.wen.ru/Shildt_Java2/