Создание множества процессов
Рассмотрим создание множества различных процессов. Поскольку это нельзя сделать с помощью предыдущего примера, то необходимо вернуться к использованию отдельных классов, унаследованных от Thread для инкапсуляции run(). Но это наиболее общее и легкое для понимания решение, так что, пока предыдущий пример показывал стиль кодирования, который вы чаще всего увидите, я не могу рекомендовать его, поскольку он несколько запутанный и не очень гибкий.
Следующий пример повторяет форму примера выше со счетчиками и кнопками переключателями. Но теперь вся информация о каком либо счетчике, включая кнопку и текстовое поле, внутри собственного объекта, который унаследован от Thread. Все поля в Ticker являются private, а это значит, что реализация Ticker может быть изменена по желанию, включая количество и тип компонентов данных для сбора и отображения информации. Когда создается объект Ticker, конструктор добавляет его визуальные компоненты на панель внешнего объекта:
//: c14:Counter4.java
// By keeping your thread as a distinct class,
// you can have as many threads as you want.
// <applet code=Counter4 width=200 height=600>
// <param name=size value="12"></applet>
import javax.swing.*; import java.awt.*; import java.awt.event.*; import com.bruceeckel.swing.*;
public class Counter4 extends JApplet { private JButton start = new JButton("Start"); private boolean started = false; private Ticker[] s; private boolean isApplet = true; private int size = 12; class Ticker extends Thread { private JButton b = new JButton("Toggle"); private JTextField t = new JTextField(10); private int count = 0; private boolean runFlag = true; public Ticker() { b.addActionListener(new ToggleL()); JPanel p = new JPanel(); p.add(t); p.add(b); // Calls JApplet.getContentPane().add():
getContentPane().add(p); } class ToggleL implements ActionListener { public void actionPerformed(ActionEvent e) { runFlag = !runFlag; } } public void run() { while (true) { if (runFlag) t.setText(Integer.toString(count++)); try { sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } } } } class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { if(!started) { started = true; for (int i = 0; i < s.length; i++) s[i].start(); } } } public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); // Get parameter "size" from Web page:
if (isApplet) { String sz = getParameter("size"); if(sz != null) size = Integer.parseInt(sz); } s = new Ticker[size]; for (int i = 0; i < s.length; i++) s[i] = new Ticker(); start.addActionListener(new StartL()); cp.add(start); } public static void main(String[] args) { Counter4 applet = new Counter4(); // This isn' t an applet, so set the flag and
// produce the parameter values from args:
applet.isApplet = false; if(args.length != 0) applet.size = Integer.parseInt(args[0]); Console.run(applet, 200, applet.size * 50); } } ///:~
Ticker содержит не только необходимые для выполнения структуры, но также способ для управления и отображения процесса. Можно создать столько процессов сколько нужно без явного создания оконного компонента.
В Counter4 объект, содержащий массив процессов Ticker, назван s. Для максимальной гибкости размер этого массива инициализируется из вне с использованием параметров апплета. Вот как параметр размера массива выглядит на странице внутри тэга апплета:
<param name=size value="20">
Здесь param, name, и value являются ключевыми словами HTML. name это то, что вы передаете в свою программу, а value может быть любой строкой, но только той, что определяет число.
Обратите внимание, что определение размера массива s выполняется внутри init() и не является частью определения s. Таким образом, вы не можете сказать какая часть класса определена (вне любого объекта):
int size = Integer.parseInt(getParameter("size")); Ticker[] s = new Ticker[size];
Можно попытаться скомпилировать данный код, но получите странную ошибку "null-pointer exception" во время выполнения. В то же время все прекрасно работает если переместить инициализацию getParameter() внутрь init( ). Среда выполнения апплетов выполняет все необходимые действия по перехвату параметров до вызова init().
К тому же данный код является одновременно и апплетом и приложением. Когда он выполняется как приложение аргумент size передается как параметр командной строки (или используется значение по умолчанию).
После того как размер массива установлен, создается новый объект Ticker, как часть конструктора, кнопка и текстовое поле добавляются к апплету.
Нажатие на кнопку start обозначает цикл по всему массиву Ticker и вызывает start() для каждого. Запомните, start() выполняет необходимую инициализацию процесса и, затем, вызывает run( ) для каждого процесса.
Слушатель ToggleL просто инвертирует флаг в Ticker и, когда связанный с ним процесс в следующий раз проверит значение, он среагирует соответственно.
Одно достоинство данного примера в том, что он позволяет вам просто создавать большое количество независимых подзадач и отслеживать их поведение. В этом случае вы увидите, что по мере того как количество подзадач становится все больше ваш компьютер, вероятнее всего, будет показывать различные значения счетчика, что связано со способом обработки процессов.
Можно также поэкспериментировать и убедиться в том, насколько sleep(100) важен внутри Tricker.run(). Если убрать sleep() все будет прекрасно работать пока вы не нажмете кнопку переключатель, что установит значение runFlag в false после чего run() просто заморозится в бесконечном цикле, который будет трудно прервать во время мульти процессорности, так что время отклика программы и скорость выполнения заметно ухудшаться.