Использование потоков
Содержание
1 Введение
2 Работа с выражениями типа Boolean
3 Класс JoptionPane
4 Приложение-счетчик
5 Ссылки
Введение
С самого начала Java платформа создавалась как многопоточная среда. Во время выполнения основной программы в фоновом режиме могут выполняться также другие процессы, такие как сборка мусора и обработка запросов. Представьте себе данные задачи в виде потоков. И хотя они управляются системой, они все же являются потоками. Потоки позволяют вам определять отдельные задачи, которые выполняются независимо друг от друга. Система, то загружает их в ЦПУ, то выгружает из него, что создает видимость их одновременного выполнения.
Потоки также можно использовать для выполнения множества задач. Их можно создавать самостоятельно или взаимодействовать с потоками системы.
Многозадачность позволяют реализовывать следующие классы и интерфейсы:
класс java.util.Timer
класс javax.swing.Timer
класс Thread
интерфейс Runnable
Для простых задач, требующих повторения, можно использовать класс java.util.Timer, указывающий на повторное выполнение задачи с интервалом в пол секунды. Большинство системных процессов учитывают время с точностью до миллисекунды.
Задача, которую должен выполнять Timer, определена в экземпляре java.util.TimerTask, в котором метод run содержит требующую выполнения задачу. Это продемонстрировано в классе Hi, в котором во время нажатия на клавишу Enter в консоль повторно выводится строка "Hi".
import java.util.*;
public class Hi {
public static void main(String args[]) throws java.io.IOException {
TimerTask task = new TimerTask() {
public void run() {
System.out.println("Hi");
}
};
Timer timer = new Timer();
timer.schedule(task, 0, 500);
System.out.println("Press ENTER to stop");
System.in.read(new byte[10]);
timer.cancel();
}
}
Среда Java Runtime Environment (JRE) устроена таким образом, что программа не завершит работы, пока запущен хотя бы один поток. Программа завершит работу, как только выполнение всех потоков пользователя будет отменено. Кроме пользовательских выполняются также системные потоки (например, сборщик мусора). Подобные потоки еще принято называть потоками-демонами. Выполнение таких потоков не может повлиять на остановку рабочей среды. За это ответственны только несистемные потоки.
Классы javax.swing.Timer и java.util.Timer работают одинаково, не считая некоторых различий. А именно, в классе javax.swing.Timer задача, требующая выполнения, определяется реализацией интерфейса ActionListener. Кроме того, задача выполняется внутри потока обработки событий, а не вне него, как в случае с классом java.util.Timer. Это очень важно для создания набора компонентов Swing.
Если вы не знаете, Swing – это набор графических компонентов, доступных Java-программам. Swing был сконструирован однопоточным. То есть, весь доступ ко внутреннему содержимому классов Swing должен быть осуществлен в пределах одного потока, которым является поток обработки событий. Например, если вы хотите изменить текст компонента label, для этого недостаточно просто вызвать метод setText Jlabel. Вместо этого вам придется удостовериться в том, что вызов метода setText происходит в потоке обработки событий, именно там и пригодится класс javax.swing.Time.
В качестве иллюстрации второго случая рассмотрим представленную далее программу, в которой отображается прирост значения счетчика. Отображаемое число увеличивается с интервалом в пол секунды.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Count {
public static void main(String args[]) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
final JLabel label = new JLabel("", JLabel.CENTER);
label.setFont(new Font("Serif", Font.PLAIN, 36));
contentPane.add(label, BorderLayout.CENTER);
ActionListener listener = new ActionListener() {
int count = 0;
public void actionPerformed(ActionEvent e) {
count++;
label.setText(Integer.toString(count));
}
};
Timer timer = new Timer(500, listener);
timer.start();
frame.setSize(300, 100);
frame.show();
}
}
Результатом выполнения данной программы будет следующее:

В тех случаях, когда выполнения повторяющихся простых задач не требуется, начинает действовать класс java.lang.Thread. Он позволит вам самостоятельно управлять функциями программы. Создав подкласс Thread, вы сможете даже при отключенной системе выполнять длительные задачи (считывание файла из сети и т.п.) не блокируя при этом выполнение остальной части программы. Данная задача длительного выполнения будет определена в методе run.
Метод run, используемый классами Thread и TimerTask, фактически является частью интерфейса java.lang.Runnable. Для определения задачи, требующей выполнения, вы всегда можете создать подкласс Thread. Тем не менее, если вы не добавляете в подкласс никаких других поведений (кроме выполняемой задачи), наиболее подходящим способом для создания системы будет определение задачи, требующей выполнения в реализации интерфейса Runnable. После этого необходимо будет передать реализацию конструктору Thread. Таким образом, при запуске потока будет выполняться именно этот интерфейс Runnable.
Во втором способе можно создать подкласс Thread и реализовать в нем метод run. Можно также реализовать метод run в классе, реализующем интерфейс Runnable, и передать эту реализацию конструктору Thread.
Язык программирования Java поддерживает только одиночное наследование. Если, кроме класса Thread, вы создаете вызов для другого суперкласса, можно сделать так, чтобы ваш класс реализовывал интерфейс Runnable и выполнял свою собственную задачу. В качестве варианта создайте подкласс Thread, чтобы он определял свой метод run, не добавляя при этом никаких дополнительных операций в процесс.
В случае, когда создается подкласс Thread, в представленной далее программе для счета количества символов в URL, задаваемом из командной строки, создается новый поток. Во время выполнения данной операции демонстрируется еще один случай реализации интерфейса Runnable – выводится повторяющееся сообщение (для этого необходимо обеспечить специальный код). Необходимо на некоторое время приостановить класс Both и выполнить действие. Сравните оба случая с вариантом использования класса Timer. В финальной части программы, для того чтобы инициировать ее завершение, вам потребуется прочитать содержимое командной строки. Заметьте, что пока система считывает URL и выводит сообщения, вы всегда можете нажать Enter для остановки программы.
import java.io.*;
import java.net.*;
public class Both {
public static void main(String args[]) {
final String urlString = args[0];
final String message = args[1];
Thread thread1 = new Thread() {
public void run() {
try {
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
InputStreamReader isr = new InputStreamReader(connection
.getInputStream());
BufferedReader reader = new BufferedReader(isr);
int count = 0;
while (reader.read() != -1) {
count++;
}
System.out.println("Size is : " + count);
reader.close();
} catch (MalformedURLException e) {
System.err.println("Bad URL: " + urlString);
} catch (IOException e) {
System.err.println("I/O Problems");
}
}
};
thread1.start();
Runnable runnable = new Runnable() {
public void run() {
while (true) {
System.out.println(message);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
}
};
Thread thread2 = new Thread(runnable);
thread2.start();
try {
System.out.println("Press ENTER to stop");
System.in.read(new byte[10]);
} catch (IOException e) {
System.out.println("I/O problems");
}
System.exit(0);
}
}
Существует множество способов работы с потоками, поэтому с технической точки зрения вы можете выбирать наиболее подходящий для вас. Для того чтобы стать хорошим Java-программистом, вовсе необязательно знать о Java и его основных библиотеках буквально все. Однако при работе с потоками дело обстоит иначе. Чем скорее вы поймете принципы работы потоков и их использования, тем скорее вы поймете общую концепцию работы и взаимодействия Java-программ.
Работа с выражениями типа Boolean
Булевское значение является примитивным типом данных и может принимать только два значения: true или false. Булевские выражения позволяют рассматривать несколько условий в одном программном решении. Для определения истинности или ложности условий булевские выражения чаще всего используют следующие операторы:
&& – логический оператор «И»
|| – логический оператор «ИЛИ»
! – логический оператор «НЕ»
^ – логический оператор «исключающее ИЛИ»
Например, в программе, определяющей оплату за сверхурочные часы, приложение должно в начале проверить, не превысило ли общее количество наработанных часов в неделю по заданному виду деятельности лимит в 40 часов.
if ((hours > 40) && (position.equals(
"salesClerk") ||
(position.equals("stockPerson"))
System.out.println(
"Employee receives overtime pay.");
else
System.out.println(
"Employee receives regular pay.");
В задаче поставленной ранее указывается, что если было наработано более 40 часов, а в качестве вида деятельности указаны или продавец или заведующий складом, то должно вывестись сообщение «Служащий получает оплату за сверхурочную работу». В противном случае выводится сообщение «Служащий получает стандартную зарплату».
В начале программного кода можно объявить булевские переменные, после чего использовать их в булевских выражениях для проверки поставленных условий:
boolean isSaturday = day.equals("Saturday");
boolean isDayLight = hour < 19 && hour > 5;
if(isSaturday && isDayLight)
System.out.println("Go play at the beach");
if (isSaturday && !isDayLight)
System.out.println("Sleep until Sunday.");

В представленном выше примере первая булевская переменная isSaturday принимает значение true только, если возвращается строка Saturday. Вторая переменная isDayLight определяет условие дневного времени и возвращает значение true только в определенные часы.
Класс JoptionPane
Такие GUI-компоненты, как всплывающие диалоговые окна, очень полезны для предупреждения пользователей о возможных проблемах, запроса информации или для предоставления инструкций. Программный интерфейс Swing обеспечивает класс JoptionPane, который позволяет вам создавать простые диалоговые окна путем вызова статического метода, а также создавать более сложные диалоги, содержащие текстовые поля и кнопки.
Существует четыре основных типа диалоговых окон JoptionPane:
диалог ввода (Input dialog)
диалог подтверждения (Confirm dialog)
диалог-сообщение (Message dialog)
диалог выбора опций (Option dialog)
Создать диалоговое окно JOptionPane можно двумя способами: вызвать один из JOptionPane конструкторов или вызвать один из статических методов JOptionPane:
showConfirmDialog: запрос подтверждения, ответы могут принимать значения типа Yes/No/Cancel.
showInputDialog: запрос информации.
showMessageDialog: сообщение пользователю о происшедшем событии.
showOptionDialog: комбинация предыдущих трех задач.

Объекты JOptionPane не являются автономными контейнерами. Напротив, они существуют внутри объектов JDialog и JinternalFrame, которые автоматически создаются и ассоциируются статическими методами.
Если во время выполнения приложения диалоговое окно появляется до реализации потока, все возможные действия приостанавливаются до тех пор, пока не будет осуществлено взаимодействие с диалогом (ввод информации, нажатие на кнопку или просто закрытие диалога).
Создать диалоговое окно, сообщающее пользователю о том, что запрашиваемый файл не существует, можно при помощи обращения к статическому методу showMessageDialog:
JOptionPane.showMessageDialog(null, "Requested file cannot be found.", null, JOptionPane.ERROR_MESSAGE );
or a dialog to confirm information:
JOptionPane.showConfirmDialog(null, "Do you want to Exit?", null, JOptionPane.YES_NO_OPTION );
Диалоговые окна JOptionPane служат для решения многих полезных задач: от получения данных от пользователя до вывода предупреждений или другой информации.
Приложение-счетчик
Напишите программу, отображающую в середине экрана число, и содержащую четыре кнопки:
Первая кнопка приводит к тому, что число нарастает через каждые пол секунды.
Вторая кнопка вызывает отрицательный прирост числа через каждые пол секунды. В определенный период времени число может или увеличиваться, или уменьшаться.
Нажатие на третью кнопку вызовет остановку режима увеличение/уменьшение.
И, наконец, в результате нажатия на четвертую кнопку результат будет сброшен к нулю.
Возможный вариант решения смотрите здесь.
Ссылки
The Java FAQ -- Threads (FAQ по использованию потоков)
Multithreaded Swing Applications (многопоточные Swing приложения)
Creating a Threaded Slide Show Applet (создание апплета с потоковым отображением слайдов)
Lesson: Threads: Doing Two or More Tasks At Once (Урок: Потоки: Одновременное выполнение двух и более задач)
Why Use Threads? (Для чего необходимо использовать потоки?)
How to Make Dialogs (Правила создания диалогов)
Class JOptionPane (Класс JOptionPane)