Пример 5.2. Доработанное многопоточное приложение
// Поток для расчета координат линии class ComputeLines implements Runnable { boolean going = true; public ComputeLines(MainApplet parentObj) { parent = parentObj; } public void compute() { int w = parent.size().width-1, h = parent.size().height-1; parent.LineCoordinates ((int)(Math.random()*w),(int)(Math.random()*h), (int)(Math.random()*w), (int)(Math.random()*h)); } MainApplet parent; // ссылка на создателя объекта public void run() { while(going) { compute(); } } }
public class MainApplet extends JApplet { ... // скопируйте из предыдущего примера ComputeLines m_lines = null; int m_lineX1 = 0, m_lineX2 = 0, m_lineY1 = 0, m_lineY2 = 0; // Синхронный метод для установки координат // прямоугольника из другого потока public synchronized void RectCoordinates(int x, int y) { m_rectX = x; m_rectY = y; this.repaint(); } // Синхронный метод для установки координат овала // из другого потока public synchronized void OvalCoordinates(int x, int y) { m_ovalX = x; m_ovalY = y; this.repaint(); } // Синхронный метод для установки координат линии // из другого потока public synchronized void LineCoordinates(int x1, int y1, int x2, int y2) { m_lineX1 = x1; m_lineX2 = x2; m_lineY1 = y1; m_lineY2 = y2; this.repaint(); } @Override public void start() { super.start(); // Запускаем потоки ... // скопируйте из предыдущего примера if (m_lines == null) { m_lines = new ComputeLines(this); new Thread(m_lines).start(); } } @Override public void stop() { super.stop(); // Останавливаем потоки ... // скопируйте из предыдущего примера if(m_lines!= null) m_lines.going = false; } public void paint(Graphics g) { ... // скопируйте из предыдущего примера g.setColor(Color.green); g.drawLine(m_lineX1, m_lineX2, m_lineY1, m_lineY2); } public static void main(String[] args) { } }
Применение анимации для мультизадачности
Одним из наиболее распространенных применений апплетов является создание анимационных эффектов типа бегущей строки, мерцающих огней и других эффектов, привлекающих внимание пользователя. Для достижения таких эффектов необходим механизм, позволяющий выполнять перерисовку всего окна апплета или его части периодически с заданным интервалом. Перерисовка окна апплета выполняется методом paint(), который вызывается виртуальной машиной Java асинхронно по отношению к выполнению другого кода апплета, если содержимое окна было перекрыто другими окнами. Для периодической перерисовки окна апплета необходимо создание потока (или нескольких потоков), которые будут выполнять рисование в окне апплета асинхронно по отношению к коду апплета. Например, можно создать поток, который периодически обновляет окно апплета, вызывая для этого метод repaint(), или рисовать из потока непосредственно в окне апплета. Также можно использовать класс таймера (этот механизм уже использовался в предыдущих работах). Таймер, по сути, тоже является потоком, выполняющимся параллельно с основным.
Рассмотрим пример апплета 5.3, который умеет сам себя перерисовывать при помощи дополнительного потока.
Пример 5.3. Самоперерисовывающийся апплет public class MainApplet extends JApplet implements Runnable { boolean m_isGoing = false; @Override public void run() { while (m_isGoing) { repaint(); try { Thread.sleep(500); } catch(InterruptedException e) { stop(); } } } @Override public void start() { super.start(); // Запускаем поток m_isGoing = true; new Thread(this).start(); } @Override public void stop() { super.stop(); // Останавливаем потоки m_isGoing = false; // Дадим потоку время завершиться try { Thread.sleep(500); } catch(InterruptedException e) {} } public void paint(Graphics g) { int w = this.getWidth(), h = this.getHeight(); g.setColor(new Color((int)(Math.random() * 255), (int)(Math.random() * 255), (int)(Math.random() * 255))); g.fillRect(0, 0, w, h); } public static void main(String[] args) { }}Класс Thread содержит несколько конструкторов и большое количество методов для управления потоков. Некоторые методы класса Thread: currentThread() - возвращает ссылку на выполняемый в настоящий момент объект класса Thread; sleep() - переводит выполняемый в данное время поток в режим ожидания в течение указанного промежутка времени; start() - начинает выполнение потока. Это метод приводит к вызову соответствующего метода run(); run() - фактическое тело потока. Этот метод вызывает после запуска потока; stop() - останавливает поток (устаревший метод);
isAlive() - определяет, является ли поток активным (запущенным и не остановленным); suspend() - приостанавливает выполнение потока (устаревший метод); resume() - возобновляет выполнение потока (устаревший метод). Этот метод работает только после вызова метода suspend(); setPriority() - устанавливает приоритет потока (принимает значение от MIN_PRIORITY до MAX_PRIORITY); getPriority() - возвращает приоритет потока; wait() - переводит поток в состояние ожидания выполнения условия, определяемого переменной условия; join() - ожидает, пока данный поток не завершит своего существования бесконечно долго или в течении некоторого времени; setDaemon() - отмечает данный поток как поток-демон или пользовательский поток. Когда в системе останутся только потоки-демоны, программа на языке Java завершит свою работу; isDaemon() - возвращает признак потока-демона.
Состояние потока Во время своего существования поток может переходить во многие состояния, находясь в одном из нижеперечисленных состояний: - Новый поток - Выполняемый поток - Невыполняемый поток - Завершенный поток Новый поток. При создании экземпляра потока этот поток приобретает состояние “Новый поток”: Thread myThread=new Thread(); В этот момент для данного потока распределяются системные ресурсы; это всего лишь пустой объект. В результате все, что с ним можно делать - это запустить: myThread.start (); Любой другой метод потока в таком состоянии вызвать нельзя, это приведет к возникновению исключительной ситуации. Выполняемый поток. Когда поток получает метод start (), он переходит в состояние “Выполняемый поток”. Процессор разделяет время между всеми выполняемыми потоками согласно их приоритетам. Невыполняемый поток. Если поток не находится в состоянии “Выполняемый поток”, то он может оказаться в состоянии “Невыполняемый поток”. Это состояние наступает тогда, когда выполняется одно из четырех условий: - Поток был приостановлен. Это условие является результатом вызова метода suspend(). После вызова этого метода поток не находится в состоянии готовности к выполнению; его сначала нужно “разбудить” с помощью метода resume(). Это полезно в том случае, когда необходимо приостановить выполнение потока, не удаляя его. Поскольку метод suspend не рекомендуется к использованию, приостановка потока должна выполняться через управляющую переменную.
- Поток ожидает. Это условие является результатом вызова метода sleep(). После вызова этого метода поток переходит в состояние ожидания в течении некоторого определенного промежутка времени и не может выполняться до истечения этого промежутка. Даже если ожидающий поток имеет доступ к процессору, он его не получит. Когда указанный промежуток времени пройдет, поток переходит в состояние “Выполняемый поток”. Метод resume() не может повлиять на процесс ожидания потока, этот метод применяется только для приостановленных потоков. - Поток ожидает извещения. Это условие является результатом вызова методаwait(). С помощью этого метода потоку можно указать перейти в состояние ожидания выполнения условия, определяемого переменной условия, вынуждая его тем самым приостановить свое выполнение до тех пор, пока данное условие удовлетворяется. Какой бы объект не управлял ожидаемым условием, изменение состояния ожидающих потоков должно осуществляться посредством одного из двух методов этого потока - notify() или notifyAll(). Если поток ожидает наступление какого-либо события, он может продолжить свое выполнение только в случае вызова для него этих методов. - Поток заблокирован другим потоком. Это условие является результатом блокировки операцией ввода-вывода или другим потоком. В этом случае у потока нет другого выбора, как ожидать до тех пор, пока не завершится команда ввода-вывода или действия другого потока. В этом случае поток считается невыполняемым, даже если он полностью готов к выполнению. Завершенный поток. Когда метод run() завершается, поток переходит в состояние “Завершенный поток”. Приоритеты потоков. В языке Java каждый поток обладает приоритетом, который оказывает влияние на порядок его выполнения. Потоки с высоким приоритетом выполняются чаще потоков с низким приоритетом. Поток наследует свой приоритет от потока, его создавшего. Если потоку не присвоен новый приоритет, он будет сохранять данный приоритет до своего завершения. Приоритет потока можно установить с помощью метода setPriority(), присваивая ему значение от MIN_PRIORITY до MAX_PRIORITY (константы класса Thread). По умолчанию потоку присваивается приоритет Thread.NORM_PRIORITY.
Потоки в языке Java планируются с использованием алгоритма планирования с фиксированными приоритетами.Этот алгоритм, по существу, управляет потоками на основе их взаимных приоритетов, кратко его можно изложить в виде следующего правила: в любой момент времени будет выполняться “Выполняемый поток” с наивысшим приоритетом. Как выполняются потоки одного и того же приоритета, в спецификации Java не описано. Группы потоков. Все потоки в языке Java должны входить в состав группы потоков. В классе Thread имеется три конструктора, которые дают возможность указывать, в состав какой группы должен входить данный создаваемый поток. Группы потоков особенно полезны, поскольку внутри их можно запустить или приостановить все потоки, а это значит, что при этом не потребуется иметь дело с каждым потоком отдельно. Группы потоков предоставляют общий способ одновременной работы с рядом потоков, что позволяет значительно сэкономить время и усилия, затрачиваемые на работу с каждым потоком в отдельности. В приведенном ниже фрагменте программы создается группа потоков под названием genericGroup (родовая группа). Когда группа создана, создаются несколько потоков, входящих в ее состав: ThreadGroup genericGroup=new ThreadGroup ("My generic group"); Thread t1=new Thread(genericGroup,this); Thread t2=new Thread(genericGroup,this); Если при создании нового потока не указать, к какой конкретной группе он принадлежит, этот поток войдет в состав группы потоков main (главная группа). Иногда ее еще называют текущей группой потоков. В случае апплета main может и не быть главной группой. Право присвоения имени принадлежит Web-браузеру. Для того чтобы определить имя группы потоков, можно воспользоваться методом getName () класса ThreadGroup. Для того, чтобы определить к какой группе принадлежит данный поток, используется метод getThreadGroup(), определенный в классе Thread. Этот метод возвращает имя группы потоков, в которую можно послать множество методов, которые будут применяться к каждому члену этой группы.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|