Отзывчивый пользовательский интерфейс
В качестве отправной точки рассмотрим программу выполняющую какие-либо интенсивные вычисления из-за чего совершенно не реагирует на ввод пользователя. Нижеприведенный код, являющийся апплетом/приложением одновременно, просто выводит показания счетчика:
//: c14:Counter1.java
// A non-responsive user interface.
// <applet code=Counter1 width=300 height=100>
// </applet>
import javax.swing.*; import java.awt.event.*; import java.awt.*; import com.bruceeckel.swing.*;
public class Counter1 extends JApplet { private int count = 0; private JButton start = new JButton("Start"), onOff = new JButton("Toggle"); private JTextField t = new JTextField(10); private boolean runFlag = true; public void init() { Container cp = getContentPane(); cp.setLayout(new FlowLayout()); cp.add(t); start.addActionListener(new StartL()); cp.add(start); onOff.addActionListener(new OnOffL()); cp.add(onOff); } public void go() { while (true) { try { Thread.sleep(100); } catch(InterruptedException e) { System.err.println("Interrupted"); } if (runFlag) t.setText(Integer.toString(count++)); } } class StartL implements ActionListener { public void actionPerformed(ActionEvent e) { go(); } } class OnOffL implements ActionListener { public void actionPerformed(ActionEvent e) { runFlag = !runFlag; } } public static void main(String[] args) { Console.run(new Counter1(), 300, 100); } } ///:~
Swing и апплеты должны быть вам уже знакомы по главе 13. Метод go() это то место программы где выполнение зацикливается: текущее значение count помещается в JTextField t, после чего count увеличивает значение.
Часть бесконечного цикла внутри go() вызов sleep(). Sleep() должен ассоциироваться с объектом Thread, и это должно показывать, что каждое приложение имеет несколько связанных с ним процессов. (Действительно, Java базируется на процессах и всегда есть один, запущенный с вашим приложением.) Таким образом, в зависимости от того где вы точно используете процессы, вы можете вызвать текущий процесс используемый программой при помощи Thread и статического sleep() метода.
Имейте ввиду, что sleep() может генерировать исключения InterruptedException, хотя генерация подобного исключения является неправильным путем выхода из процесса и должна быть отвергнут. (Повторю еще раз, исключения существуют только для особых ситуаций, а не для управления выполнения программы.) Вызов спящего потока включен для поддержки будущих расширений языка.
Когда нажата кнопка Strart выполняется go(). Глянув на код go() вы можете наивно предположить (как и я), что множественность процессов будет соблюдаться, так как процесс засыпает. Таким образом, когда данный метод заснул, CPU должен заниматься опросом других кнопок. На самом деле проблема в том, что go() никогда не завершиться, поскольку цикл бесконечный, а значит actionPerformed( ) не завершиться. Поскольку вы находитесь в actionPerformed( ) после первого нажатия, программа не сможет обработать другие события. (Для выхода необходимо каким-то образом завершить приложение, наиболее простой способ нажать Ctrl+C в консольном окне, если запущено в консоли. Если запущено в броузере, то придется убить броузер.)
Основная проблема заключается в том, что go() должна продолжить выполнение и в то же время завершить выполнение так, чтобы вызов actionPerformed( ) мог завершиться и пользовательский интерфейс мог снова среагировать на действия пользователя. Но обычный метод, похожий на go(), не может продолжить выполнение и вернуть управление основной программе одновременно. Это звучит как неразрешимая проблема, как будто CPU должен находиться сразу в двух местах, но это точно иллюзия создаваемая процессами.
Модель процессов (и ее программирование, поддерживаемое Java) удобное средство программирования для облегчения запуска нескольких операций в одно и то же время в одной программе. С процессами CPU обходит их всех и выделяет каждому квант времени. Каждый процесс считает, что выполняется на CPU единолично, на самом деле время процессора поделено между всеми процессами. Исключением является случай, когда программа запущено на многопроцессорной машине. Но одно важное обстоятельство насчет процессов заключается в том, что вам не нужно думать об этих уровнях, так что коду вашей программы не обязательно знать выполняется он на единственном CPU или на нескольких. Таким образом, процессы дают возможность создавать легко масштабируемые приложения.
Процессы немного уменьшают эффективность вычислений, но улучшенные сетевые возможности, т.к. сбалансированность ресурсов и удобство пользователя зачастую более важны. Конечно, если вы имеете более одного процессора, то операционная система позволяет выделить каждый CPU для нескольких процессов и вся программа будет выполняться значительно быстрее. Многозадачность и множественность процессов будут более предпочтительными для использования многопроцессорных систем.