Главная » Java, Web » Потоки в JAVA примеры

0

Таймер скрывает в себе потоки. Таким образом, работа с ними становится опосредованной. Однако могут возникнуть такие ситуации, когда необходимо обратиться к потокам и использовать их в явном виде. О потоках будет рассказано на примере. Этот пример представляет собой вполне реалистичную задачу, которая требует большого количества вычислений, связанных с построением графического образа фрактального множества, в данном случае это будет множество Мандельброта. Строгое определение фрактального множества состоит в констатации того факта, что топологическая размерность этого множества должна быть строго меньше его хаусдорфовой размерности.

Вычисления производятся на основе следующего алгоритма. Построение начинается в точке (.v, у), где .v и у — это вещественные числа. Вычисляем новую точку = .v, zy = У- повторяем процесс замены точки (.^v, zy) новым значением (z х х х zx — zy х zy + х, 2zx х zy + у).

Для построения картинки чуть иначе сформулируем задачу. Выберем точку (х, у) и зададимся вопросом, сколько шагов (не более заданного) необходимо для того, чтобы эта точка, при использовании описанной выше процедуры, удалилась от точки {0, 0) на определенное расстояние? Затем мы зададим цвет точки в соответствии с тем, какое количество шагов потребуется. Проделав эту процедуру для набора точек, мы получим некоторую картинку. Приводим текст апплета (листинг 1.34), который производит такие вычисления (рис. 1.28). Апплет начинает работать после нажатия кнопки Start. В апплете представлена область плоскости, ограниченная условиями -1.25 <х< 1.0 и -1.25 <у< 1.25.

Программа работает по следующему алгоритму:

Для квадратов размерами 64, 32, 16, 8, 4, 2, и 1: Для каждого квадрата в апплете: Пусть (а,Ь) — координаты центра квадрата. Пусть (х,у) — вещественные числа, соответствующие (а,Ь). Пусть (zx, zy) = (х, у). Пусть count = 0.

Повтор до тех пор, пока count = 80 или (zx,zy) стало "большим":

Пусть new zx = zx*zx — zy*zy + x.

Пусть zy = 2*zx*zy + y.

Пусть zx = new zx.

Пусть count = count + 1.

Пусть color = Color.getHSBColor(count/100.OF, 0.0F, 0.0F). Заполнить квадрат заданным цветом.

На практике оказывается, что это вычисление требует много времени. I Листинг 1.34. Файл Mandelbrot.java

impo гt j ava.awt.*; impo rt j ava.awt.event.*; import javax.swing.*;

public class Mandelbrot extends JApplet { Display canvas;

//на "холсте" будет отображаться множество Мандельброта JButton stopButton, startButton; // вычисления начинаются после

// нажатия кнопки Start и // продолжаются до тех пор, / / пока не нажата кнопка Stop

public void init() {

// инициализация апплета setBackground(Color.gray); canvas = new Display();

getContentPane().add(canvas, BorderLayout.CENTER);

JPanel bottom = new JPanel();

bottom.setBackground(Color.gray) ;

startButton = new JButton("Start");

startButton.addActionListener(canvas);

bottom.add(startButton);

stopButton = new JButton("Stop");

stopButton.addActionListener(canvas);

bottom.add(stopButton);

stopButton.setEnabled(false);

getContentPane().add(bottom, BorderLayout.SOUTH); }

public Insets getlnsets() {

// оставляем место вдоль границы апплета,

// заполненное цветом фона (в данном случае – серым)

return new Insets(2,2,2,2); }

public void stop() {

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

canvas.stopRunning(); }

// следующий вложенный класс рисует //и производит необходимые вычисления

class Display extends JPanel implements ActionListener, Runnable { Image OSI; // Изображение (не на экране), в котором хранится

// картинка, соответствующая множеству Мандельброта.

// Изображение создается вычислительным потоком, //а затем копируется на холст. Graphics OSG; // графический контекст Thread runner; // поток вычислений

boolean running; // значение true, если поток работает

double xmin = -2.5; // интервал координат

double xmax = 1;

double ymin = -1.25;

double ymax = 1.25;

public void paintcomponent(Graphics g) { // Вызывается системой для рисования. // Копирует рисунок на экран // (если рисунок существует)

// если рисунка нет, то заполняет экран черным цветом, if (OSI == null) { g.setColor(Color.black) ;

g.fillRect(0,0,getwidth(),getHeight()) ; }

else {

g.drawlmage(OSI,0,0,null); }

}

public void actionPerformed(ActionEvent evt) { // Вызывается в том случае,

// если пользователь нажимает кнопку "Start" // или кнопку "Stop". //В ответ анимация либо запускается, // либо останавливается. String command = evt.getActionCommand(); if (command.equals("Start")) startRunning(); else if (command.equals("Stop"))

stopRunning(); }

void startRunning() { // простой метод, который запускает поток вычислений // (если он еще не запущен) if (running) return;

runner = new Thread(this) ; running = true; runner.start () ;

}

void stopRunning() { // метод, останавливающий поток вычислений running = false;

}

int countlterations(double x, double y) { // Множество Мандельброта представлено с использованием // различных цветов, которыми раскрашены квадраты //в зависимости от того, сколько шагов требуется // произвести до того, как точка достигнет заданного // расстояния. Максимальное число // шагов установлено равным 80. Прочие цвета // относятся к точкам, не входящим в множество Мандельброта. int count = 0; double zx = x; double zy = y;

while (count < 80 && Math.abs(x) < 100 && Math.abs(zy) < 100) { double new_zx = zx*zx — zy*zy + x; zy = 2*zx*zy + y; zx = new zx;

count++; }

return count;

}

int i,j; // центральный пиксел отображаемого квадрата int size; // размер отображаемого квадрата

int colorlndex; // цвет квадрата — число в пределах от 1 до 80, // соответствующее свойствам // центральной точки квадрата Runnable painter = new Runnable() {

// объект Runnable, который рисует квадрат, //а затем копирует его на экран public void run() { int left = i — size/2; int top = j — size/2;

OSG.setColor(Color.getHSBCoior(colorlndex/100.OF,IF, IF));

OSG.fillRect(left,top,size,size); paintlmmediately(left,top,size,size);

} };

public void run() {

// Этот метод запускает поток вычислений. Он рисует множество // Мандельброта, которое формируется в несколько проходов // с увеличением разрешения картинки. С каждым разом // сторона прорисовываемого квадрата уменьшается вдвое. startButton.setEnabled(false); // выключить кнопку "Start" stopButton.setEnabled(true); // включить кнопку "Stop"

//на время работы потока int width = getWidth(); // текущий размер холста int height = getHeight(); OSI = createlmage(getWidth(),getHeight());

// Создать рисунок за экраном для хранения изображения. // Сначала картинка заполнена черным цветом. OSG = OSI.getGraphics(); OSG.setColor(Color.black); OSG.fillRect(0,0,width,height);

for (size = 64; size >= 1 && running; size = size/2) { double dx,dy; // размер апплета dx = (xmax — xmin)/width * size; dy = (ymax — ymin)/height * size;

double x = xmin + dx/2; // х-координата центра квадрата for (i = size/2; i < width+size/2 && running; i += size) {

// цикл рисования одного столбца квадратов double у = ymax — dy/2; // у-координата центра квадрата for (j = size/2; j < height+size/2 && running; j += size) { // самый нижний цикл рисования квадрата, // просчет числа итераций для определения цвета, // вызов "painter" для рисования colorlndex = countlterations(х,у); try {

SwingUtilities.invokeAndWait(painter); }

catch (Exception e) { }

у -= dy;

}

х += dx;

Thread.yield(); // дать шанс другому потоку

}

}

running = false; // поток завершен, поскольку либо // вычисления завершены, // либо выполнение потока где-либо // прекращено

startButton.setEnabled(true); // восстановление состояний

// кнопок

stopButton.setEnabled(false);

}

}

Рис. 1.28. Отображение множества Мандельброта

в Java принадлежат классу j ava. lang. Thread. Назначение потока — независимое выполнение самостоятельного фрагмента программы параллельно с остальной программой. В случае с программой рисования множества Мандельброта, этот кусок программы реализует алгоритм, который был приведен выше. Как правило, в потоке используется метод

public void run();

Этот метод определяется для объекта, в котором имплементируется интерфейс Runnabie. Интерфейс Runnabie описывает единственный метод run(). Объект Runnabie будет являться параметром конструктора объекта потока. Можно определить поток как подкласс класса Thread и определить метод run о в подклассе, но удобнее пользоваться объектом Runnabie. Если этот объект является объектом, имплементирующим интерфейс Runnabie, то поток можно создать с помощью команды:

Thread runner = new Thread(runnableObject);

Задача этого потока — выполнение метода run (). Как и в случае с таймером, создание потока — это не полное решение задачи. Необходимо запустить поток, для этого вызывается метод runner.start () . Вызов этого метода приводит к запуску потока, выполняется метод run (), выполнение его происхождит параллельно с остальной частью программы. Для остановки потока не существует метода, аналогичного методу stop () в таймере, который бы приводил к остановке выполнения потока. Если мы хотим иметь возможность остановить поток, то необходимо позаботиться о создании такого механизма, который бы позволил сообщить потоку о том, что следует прекратить выполнение инструкций потока. Это можно сделать, например, задав переменную running, которая видима как в методе run о, так и в любой другой части программы. Когда появляется необходимость остановить поток, этой переменной присваивается значение false. В методе run о происходит регулярная проверка значения этой переменной. Если обнаруживается, что значение этой переменной становится равным false, то выполнение метода run () должно быть прекращено. Вот, например, как происходит остановка потока в апплете, отображающим множетсво Мандельброта:

void startRunning() { // Метод, запускающий поток вычислений. // Этот метод используется только один раз // при нажатии кнопки "Start", после чего кнопка // выключается, а поток начинает работу, if (running) return; runner = new Thread(this) ;

// создает поток, который будет выполнять метод run() // класса, имплементирующего интерфейс Runnabie running = true;

runner.start () ;

}

void stopRunning() {

// Простой метод, который останавливает выполняемый поток // с вычислениями.

// Это происходит посредством установления значения // переменной running, которое становится равным false. // Поток регулярно проверяет значение этой переменной //и при необходимости прекращает работу. running = false;

}

На некоторых платформах после начала своей работы поток забирает контроль над процессором и не позволяет другим потокам начать работать до тех пор, пока не отдаст его, для чего используется тактический метод Thread.yield (). Полезно использовать этот метод, чтобы не возникли трудности в дальнейшем. Существует и другой метод предоставить возможность другому потоку приступить к работе, при этом текущий поток "засыпает" на некоторое время, указываемое в качестве аргумента при обращении к методу Thread, sleep (time) . Здесь время time, отводимое для "сна", выражается в миллисекундах. В таймере, например, поток "спит" в перерывах от наступления одного события до другого. При работе с потоками совместно с компонентами Swing следует быть осторожными. Лучше не применять компоненты или используемые ими данные при работе с потоками, за исключением случаев работы с потоками обработки событий. можно использовать только в методах обработки событий или в методе paintComponent () . Если более одного потока будет работать с данными пакета swing, то эти данные могут быть испорчены. Для решения этой проблемы существует специально разработанный механизм, swing позволяет другому потоку осуществлять доступ к потоку обработки событий, запуская для этого функции. Функция должна представлять собой метод run () объекта Runnabie. Когда поток вызывает метод

void SwingUtilities.invokeAndWait(Runnabie runnableObject)

то метод run() будет выполнен в потоке обработки событий, и здесь он безопасно может делать все с компонентами swing и соответствующими им данными. Метод run () в апплете Мандельброта использует метод

SwingUtilities. invokeAndWait () ДЛЯ заполнения заданным цветом ТОГО или иного квадрата, изображаемого на холсте апплета (сначала за экраном, потом изображение переносится на экран). Весь метод run о выглядит следующим образом:

public void run() { startButton.setEnabled(false);

stopButton.setEnabled(true);

int width = getWidth();

int height = getHeight();

OSI = createimage(getWidth(),getHeight());

OSG = OSI.getGraphics();

OSG.setColor(Color.black);

OSG.fillRect(0,0,width,height);

for (size = 64; size >= 1 && running; size = size/2) { double dx,dy;

dx = (xmax — xmin)/width * size; dy = (ymax — ymin)/height * size; double x = xmin + dx/2;

for (i = size/2; i < width+size/2 && running; i += size) { double у = ymax — dy/2;

for (j = size/2; j < height+size/2 && running; j += size) { colorlndex = countlterations(x,y); try {

SwingUtilities.invokeAndWait(painter); }

catch (Exception e) { }

у -= dy;

}

x += dx; Thread.yield();

}

}

running = false; startButton.setEnabled(true); stopButton.setEnabled(false) ;

}

Источник: Будилов В. А. Интернет-программирование на Java. — СПб.: БХВ-Петербург, 2003. — 704 е.: ил.

По теме:

  • Комментарии