Назад
Вперед
7.13. Конвейерные потоки
На примере аплета мы демонстрируем
сихронизацию двух исполняемых потоков,
созданных на базе класса Thread, с применением
конвейерных потоков класса PipedOutputStream и PipedInputStream.
Один исполняемый поток передает другому через
конвейер объекты класса Sinusoid, определенного в
приложении. Для этого на базе конвейерных
потоков мы создаем потоки классов ObjectOutputStream и
ObjectInputStream.
Исходный текст примера
Архив проекта для Java WorkShop 2.0
Демонстрация
(ваш браузер должен уметь работать с аплетами
Java JDK 1.1)
Немного теории
В библиотеке классов java.io.* определены классы
PipedOutputStream и PipedInputStream, с помощью которых можно
организовать конвейерную передачу данных.
Конвейерные потоки создаются парами, причем
конструктору одного из них в качестве параметра
передается ссылка на другой. Например, вы можете
вначале создать выходной конвейерный поток
класса PipedOutputStream, а затем дополнить его парой -
входным конвейерным потоком класса PipedInputStream:
PipedOutputStream pout;
PipedInputStream pin;
try
{
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
В классах PipedOutputStream и PipedInputStream определены,
соответственно, методы write и read, с помощью которых
можно работать с примитивными типами данных.
Если же вам нужно передавать через конвейерные
потоки данные стандартных типов Java или объекты
произвольных классов, реализующих интерфейс
Serializable, создайте на базе этих потоков потоки
класса DataOutputStream, DataInputStream, ObjectOutputStream и ObjectInputStream.
Конвейерные потоки удобно использовать для
синхронизированной передачи данных из одного
потока выполнения в другой. При чтении данных из
пустого входного конвейерного потока поток
выполнения будет блокирован, пока там не
появятся данные. Эти данные должны быть записаны
в выходной конвейерный поток, привязанный к
указанному входному конвейерному потоку, другим
потоком выполнения.
Описание примера
Наш аплет создает два потока выполнения,
работающих в цикле, и два конвейерных потока
передачи данных классов PipedOutputStream и PipedInputStream.
Первый поток выполнения, созданный на базе
класса Thread, создает объекты класса Sinusoid,
описывающие фрагменты синусоид с произвольной
частотой, амплитудой, фазовым сдвигом и цветом
для отображения. Эти объекты записываются в
цикле в выходной поток ObjectOutputStream, созданный на
базе выходного конвейерного потока класса
PipedOutputStream.
Второй поток выполнения также создан на базе
класса Thread. Он читает в цикле и рисует в окне
аплета объекты из потока класса ObjectInputStream,
созданного на базе входного конвейерного потока
PipedInputStream. Последний привязан к выходному
конвейерному потоку, в который записываются
объекты класса Sinusoid.
Таким образом, первый поток выполнения
передает второму через конвейер объекты класса
Sinusoid, а второй их отображает в окне аплета (рис. 1).
Рис. 1. Объекты класса Sinusoid в окне аплета
Рассмотрим исходный текст аплета.
Главный класса аплета Pipes
Главный класса нашего аплета создан на базе
класса Applet:
import java.applet.*;
import java.awt.*;
import java.io.*;
public class Pipes extends Applet
{
. . .
}
В нем мы определили четыре поля.
Поля pushThread и popThread хранят ссылки на потоки
выполнения, созданные на базе класса Thread:
PushThread pushThread = null;
PopThread popThread = null;
Первый из них (класса PushThread) выполняет запись
данных в конвейерный поток, а второй (класса
PopThread) - извлечение этих данных и рисование
соответствующих синусоид в окне аплета.
В полях pout и pin хранятся ссылки на выходной и
входной конвейерный поток, соответственно:
PipedOutputStream pout;
PipedInputStream pin;
Метод init главного класса аплета
Этот метод получает управление при
инициализации аплета. В его задачу входит
создание выходного потока pout класса PipedOutputStream, а
также входного потока pin класса PipedInputStream:
public void init()
{
try
{
pout = new PipedOutputStream();
pin = new PipedInputStream(pout);
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
Обратите внимание, что поток pout создается как
дополнение к потоку pin. В результате данные,
записанные в поток pout могут быть прочитаны из
потока pin.
Метод destroy главного класса аплета
При завершении работы аплета метод destroy
закрывает конвейреные потоки, вызывая для них
метод close:
public void destroy()
{
try
{
pout.close();
pin.close();
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
Метод start главного класса аплета
Когда страница HTML с аплетом отображается в окне
браузера, управление передается методу start.
Первый делом этот метод создает задачу,
выполняющую запись объектов в конвейер:
if(pushThread == null)
{
pushThread = new PushThread(pout, this);
pushThread.setPriority(Thread.MIN_PRIORITY);
pushThread.start();
}
Мы устанавливаем минимальный приоритет
соответствующего потока выполнения pushThread, чтобы
другой поток выполнения успевал опорожнять
конвейер.
Далее запускается поток выполнения, читающий
данные из конвейера:
if(popThread == null)
{
popThread = new PopThread(pin, this);
popThread.start();
}
Метод stop главного класса аплета
Когда страница с аплетом исчезает из окна
браузера, метод stop останавливает работу потоков
выполнения:
public void stop()
{
if(pushThread != null)
{
pushThread.stop();
pushThread = null;
}
if(popThread != null)
{
popThread.stop();
popThread = null;
}
}
Класс Sinusoid
Этот класс определяет поведение фрагментов
синусоид, отображаемых в окне аплета.
Класс Sinusoid реализует интерфейс Serializable:
class Sinusoid
implements Serializable
{
. . .
}
В результате объекты этого класса можно
сохранять в потоке класса ObjectOutputStream и затем
восстанавливать из потока класса ObjectInputStream.
Массивы координат точек, составляющих фрагмент
синусоиды, хранятся в полях xPoints и yPoints:
int[] xPoints;
int[] yPoints;
Количество точек записано в поле nPoints, а цвет
для рисования синусоиды - в поле clr:
int nPoints;
Color clr;
Метод paint класса Sinusoid
С помощью метода paint можно нарисовать синусоиду
в контексте отображения, передаваемом этому
методу через единственный параметр:
public void paint(Graphics g)
{
g.setColor(clr);
g.fillPolygon(xPoints, yPoints, nPoints);
}
Перед рисованием в контексте устанавливается
цвет из поля clr. Сам процесс рисования синусоиды
выполняется методом fillPolygon.
Метод setRandomShape класса Sinusoid
Этот метод выполняет начальную инициализацию
объекта класса Sinusoid, выбирая случайным образом
некоторые параметры.
Прежде всего устанавливается количество точек,
составляющих фрагмент синусоиды:
nPoints = dm.width;
Далее создаются массивы для хранения координат
указанных точек:
xPoints = new int[nPoints];
yPoints = new int[nPoints];
На следующем шаге мы определяем случайным
образом некоторые параметры, определяющие
внешний вид синусоиды:
int maxAngle = (int)((30 * Math.PI) *
Math.random());
int Amplitude = (int)(dm.height *
Math.random());
int Offset = (int)(dm.height *
Math.random());
double deltaFi = (Math.PI / 2) *
Math.random();
В соответствии с этими параметрами выполняется
заполнение массивов координатами точек,
составляющих синусоиду:
for(int i=0; i < nPoints; i++)
{
xPoints[i] = i;
yPoints[i] = Offset + (int)(Amplitude *
Math.sin(deltaFi +
(double)((maxAngle * i)) / nPoints));
}
Цвет для отображения выбирается случайным
образом и сохраняется в поле clr:
int rColor, gColor, bColor;
rColor = (int)(255 * Math.random());
gColor = (int)(255 * Math.random());
bColor = (int)(255 * Math.random());
clr = new Color(rColor, gColor, bColor);
Класс PushThread
Этот класс представляет собой поток
выполнения, записывающий объекты класса Sinusoid в
выходной конвейерный поток pout класса PipedOutputStream.
Класс PushThread создан на базе класса Thread:
class PushThread extends Thread
{
. . .
}
В нем определены два поля:
ObjectOutputStream oos;
Dimension dm;
Первое из них хранит ссылку на выходной поток
класса ObjectOutputStream, созданный на базе потока
PipedOutputStream, а во второе мы запишем размеры окна
аплета.
Конструктор класса PushThread
Конструктор класса PushThread решает две задачи:
определяет размеры окна аплета, сохраняя их в
поле dm, и создает выходной поток класса ObjectOutputStream
на базе выходного конвейерного потока класса
PipedOutputStream, передаваемого конструктору через
параметр:
public PushThread(PipedOutputStream pout,
Applet appl)
{
dm = appl.getSize();
try
{
oos = new ObjectOutputStream(pout);
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
Метод run класса PushThread
Этот метод работает в рамках отдельного потока
выполнения.
Прежде всего метод run определяет ширину окна
аплета, сохраняя ее в поле nPoints:
int nPoints = dm.width;
Далее запускается бесконечный цикл:
while(true)
{
. . .
}
При каждой итерации мы создаем новый объект
класса Sinusoid, инициализируя его методом setRandomShape:
Sinusoid s = new Sinusoid();
s.setRandomShape(dm);
Созданный таким образом объект записывается в
выходной поток oos класса ObjectOutputStream, созданный на
базе выходного конвейерного потока класса
PipedOutputStream:
try
{
oos.writeObject(s);
oos.flush();
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
После этого метод run выполняет задержку своей
работы на 30 миллисекунд:
try
{
Thread.sleep(30);
}
catch (InterruptedException e)
{
stop();
}
Далее бесконечный цикл продолжает свою работу.
Класс PopThread
Извлечение объектов класса Sinusoid из входного
конвейерного потока и их рисование в окне аплета
выполняется при помощи класса PopThread, созданного
нами на базе класса Thread:
class PopThread extends Thread
{
. . .
}
В классе определено три поля:
Graphics g;
ObjectInputStream ois;
Sinusoid s;
В поле g хранится ссылка на контекст
отображения, в котором будет нарисован фрагмент
синусоиды.
Поток ois класса ObjectInputStream создается на базе
конвейерного входного потока класса PipedInputStream.
В поле s класса Sinusoid мы храним объекты,
извлеченные из конвейерного потока.
Конструктор класса PopThread
Прежде всего конструктор класса PopThread получает
ссылку на контекст отображения, пользуясь для
этого ссылкой на аплет, передаваемой
конструктору через второй параметр:
public PopThread(PipedInputStream pis,
Applet Appl)
{
g = Appl.getGraphics();
try
{
ois = new ObjectInputStream(pis);
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
}
Далее конструктор создает входной поток для
записи объектов класса Sinusoid.
Метод run класса PopThread
Всю работу метод run класса PopThread выполняет в
бесконечном цикле.
На каждом проходе он вначале извлекает их
входного конвейера объект класса Sinusoid, а затем
рисует его в окне аплета методом paint:
try
{
s = (Sinusoid)ois.readObject();
}
catch (IOException ex)
{
System.out.println(ex.toString());
}
catch (ClassNotFoundException ex)
{
System.out.println(ex.toString());
}
s.paint(g);
Здесь вызывается метод paint, определенный в
класса Sinusoid.
Назад Вперед |