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

Библиотека примеров приложений Java

Оглавление
Выбор файлов
Простейший редактор текста
Копирование файлов UNICODE
Сохранение объекта Java в файле
Произвольные классы и файлы
Буферизация потоков
Разбор конфигура-
ционного файла

Работа с консолью
Работа с классом PrintWriter
Разбор строк класса String
Загрузка и просмотр изображений
Потоки в оперативной памяти
Конвейерные потоки
Комбинирование двух потоков
Комбинирование нескольких потоков
Поиск слова в текстовом файле
Произвольный доступ к файлу
Информация о файле
Работа с каталогами
Просмотр содержимого каталога
Просмотр каталога с фильтром
Панель для выбора каталога
Список системных свойств
Сохранение списка системных свойств
Контрольная сумма файла
Копирование, переименование, удаление файлов
Архивы ZIP
Создание архива ZIP
Распаковка архива ZIP
Обход дерева каталогов

Назад Вперед

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).

pic1.gif (11796 bytes)

Рис. 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.


Назад Вперед

[Назад]