К большинству современных распределенных приложений (Rich Client) и веб-приложений (Thin Client) выдвигаются требования одновременной поддержки многих пользователей, каждому из которых выделяется отдельный поток, а также разделения и параллельной обработки информационных ресурсов.
Потоки — средство, которое помогает организовать одновременное выполнение нескольких задач, каждой в независимом потоке. Потоки представляют собой экземпляры классов, каждый из которых запускается и функционирует самостоятельно, автономно (или относительно автономно) от главного потока выполнения программы. Существует два способа создания и запуска потока: на основе расширения класса Thread или реализации интерфейса Runnable:
package by.bsu.threads; public class TalkThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("Talking"); try { Thread.sleep(7); // остановка на 7 миллисекунд } catch (InterruptedException e) { System.err.print(e); } } } }
При реализации интерфейса Runnable необходимо определить его единственный абстрактный метод run(). Например:
package by.bsu.threads; public class WalkRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("Walking"); try { Thread.sleep(7); } catch (InterruptedException e) { System.err.println(e); } } } }
package by.bsu.threads; public class WalkTalk { public static void main(String[ ] args) { // новые объекты потоков TalkThread talk = new TalkThread(); Thread walk = new Thread(new WalkRunnable()); // запуск потоков talk.start(); walk.start(); // WalkRunnable w = new WalkRunnable(); // просто объект, не поток // w.run(); или talk.run(); // выполнится метод, но поток не запустится! } }
Запуск двух потоков для объектов классов TalkThread непосредственно и WalkRunnable через инициализацию экземпляра Thread приводит к выводу строк: Talking Walking. Порядок вывода, как правило, различен при нескольких запусках приложения. Интерфейс Runnable не имеет метода start(), а только единственный метод run(). Поэтому для запуска такого потока, как WalkRunnable следует создать экземпляр класса Thread с передачей экземпляра WalkRunnable его конструктору. Однако при прямом вызове метода run() поток не запустится, выполнится только тело самого метода.
Жизненный цикл потока
При выполнении программы объект класса Thread может быть в одном из четырех основных состояний: «новый», «работоспособный», «неработоспособный» и «пассивный». При создании потока он получает состояние «новый» (NEW) и не выполняется. Для перевода потока из состояния «новый» в состояние «работоспособный» (RUNNABLE) следует выполнить метод start(), который вызывает метод run() — основной метод потока.
Поток может находиться в одном из состояний, соответствующих элементам статически вложенного перечисления Thread.State:
NEW — поток создан, но еще не запущен;
RUNNABLE — поток выполняется;
BLOCKED — поток блокирован;
WAITING — поток ждет окончания работы другого потока;
TIMED_WAITING — поток некоторое время ждет окончания другого потока;
TERMINATED — поток завершен.
Получить текущее значение состояния потока можно вызовом метода getState().
Поток переходит в состояние «неработоспособный» в режиме ожидания (WAITING) вызовом методов join(), wait(), suspend() (deprecated-метод) или методов ввода/вывода, которые предполагают задержку. Для задержки потока на некоторое время (в миллисекундах) можно перевести его в режим ожидания по времени TIMED_WAITING) с помощью методов yield(),sleep(longmillis), join(long timeout) и wait(long timeout), при выполнении которых может генерироваться прерывание InterruptedException. Вернуть потоку работоспособность после вызова метода suspend() можно методом resume() (deprecatedметод), а после вызова метода wait() — методами notify()или notifyAll(). Поток переходит в «пассивное» состояние (TERMINATED), если вызваны методы interrupt(), stop() (deprecated-метод) или метод run() завершил выполнение, и запустить его повторно уже невозможно. После этого, чтобы запустить поток, необходимо создать новый объект потока. Метод interrupt() успешно завершает поток, если он находится в состоянии «работоспособный». Если же поток неработоспособен, например, находится в состоянии TIMED_WAITING,
то метод инициирует исключение InterruptedException. Чтобы это не происходило, следует предварительно вызвать метод isInterrupted(), который проверит возможность завершения работы потока. При разработке не следует использовать методы принудительной остановки потока, так как возможны проблемы с закрытием ресурсов и другими внешними объектами.
Методы suspend(), resume() иstop() являются deprecated-методами и запрещены к использованию, так как они не являются в полной мере «потокобезопасными».