Освоюємо Java/Паралелізм
Паралелізм в програмуванні (англ. Concurrency) — це створення програм у вигляді кількох виконуваних, паралельно-взаємодійних між собою гілок(частин) програми. Розрізняють процеси (processes) та нитки (threads). Частина перекладачів перекладають thread, як "потоки виконання", проте в ряді випадків цей переклад призводить до плутанини через існування потоків вводу/виводу (I/O Streams). Нитки є паралельно-виконуваними частинами процесу. Процеси — більш унезалежненні від інших процесів, ніж нитки від інших ниток. Зокрема, кожен процес має власний адресний простір у пам'яті комп'ютера, нитки ж одного процесу використовують спільний адресний простір пам'яті.
У програмуванні на Java, програмістам зазвичай, приходиться мати справу із окремими нитками. Багатонитковість (multithreading) є важливою можливістю Java. Кожна програма Java має хоча б одну нитку виконання. Виконання програми розпочинається із створенням головної нитки (main thread).
При роботі з нитками, програміст може або в ручну керувати ними, або ж використовувати спеціальний менеджер виконувач (executor). Спочатку, необхідно розглянути перший спосіб управління нитками виконання.
Створення ниток виконання
ред.Кожна нитка це екземпляр класу Thread. Щоб мати змогу створити нову нитку та оперувати нею, зрозуміло, що необхідний код для виконання у цій нитці. Це можна зробити двома шляхами:
- через реалізацію (implements) інтерфейсу
Runnable
, що робиться через визначення методуrun
:
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Привіт - це нитка!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
- другий спосіб полягає у створенні підкласу для Thread. Клас
Thread
уже містить реалізацію інтерфейсуRunnable
. Проте його реалізація методуrun
— порожня. Тож програміст у своєму коді просто заміщає методrun
:
public class HelloThread extends Thread {
public void run() {
System.out.println("Привіт - це нитка!");
}
public static void main(String args[]) {
(new HelloThread()).start();
}
}
Як бачимо в обох випадках для створення нової нитки викликається метод Thread.start
.
Більш кращим для застосування є перший спосіб, оскільки він дозволяє нам розширити ще якийсь клас у нашому класі (У Java відсутнє множинне успадкування, тож не можливо розширити декілька класів).
Призупинення та переривання нитки
ред.Виконання нитки можна призупинити за допомогою методу sleep
класу Thread
. Можливо, також, перервати виконання нитки з іншої нитки за допомогою методу interrupt
. Скориставшись методом join
, ми можемо задати час очікування на завершення виконання іншої нитки (безкінечно або вказаний час). Наступний приклад демонструє роботу двох ниток. Основна(main
) нитка запускає виконання іншої нитки, яка просто виводить важливе повідомлення по одному рядку через кожні 4 секунди. Основна нитка виконання кожної секунди виводить повідомлення, що вона все ще очікує на завершення породженої нитки. Якщо виконання породженої нитки триває більше періоду часу, що заданий у змінній patience
(терпіння), то нитка перериває виконання породженої нитки (закінчується терпіння). При перериванні породженої нитки, у ній виникає виняток типу InterruptedException
, який обробляється нею за допомогою конструкції try-catch
.
package ua.wikibooks.oj;
public class ThreadInteruption {
// Display a message, preceded by
// the name of the current thread
static void threadMessage(String message) {
String threadName =
Thread.currentThread().getName();
System.out.format("%s: %s%n",
threadName,
message);
}
private static class MessageLoop
implements Runnable {
public void run() {
String importantInfo[] = {
"У діброві - дуби,",
"Під дубами - гриби,",
"Трава - між грибами,",
"Хмарки - над дубами."
};
try {
for (int i = 0;
i < importantInfo.length;
i++) {
// Пауза на 4 секунди
Thread.sleep(4000);
// Надруквати повідомлення
threadMessage(importantInfo[i]);
}
} catch (InterruptedException e) {
threadMessage("Виконання задачі не завершене!");
}
}
}
public static void main(String args[])
throws InterruptedException {
// Пауза, у мілісекундах
// перед тим як буде перервано MessageLoop
// thread (по замовчуванню одна година).
long patience = 1000 * 60 * 60;
// Якщо наявний аргумент в командному рядку,
// задати терпіння (patience) в секундах
if (args.length > 0) {
try {
patience = Long.parseLong(args[0]) * 1000;
} catch (NumberFormatException e) {
System.err.println("Аргумент повинен бути цілим числом.");
System.exit(1);
}
}
threadMessage("Старт MessageLoop thread");
long startTime = System.currentTimeMillis();
Thread t = new Thread(new MessageLoop());
t.start();
threadMessage("Чекаю допоки MessageLoop thread не закінчить");
// loop until MessageLoop
// thread exits
while (t.isAlive()) {
threadMessage("Все ще очікую...");
// Почекати 1 секунду
// на закінчення MessageLoop thread
t.join(1000);
if (((System.currentTimeMillis() - startTime) > patience)
&& t.isAlive()) {
threadMessage("Терпіння закінчилось, більше не чекатиму!");
// перервати виконання нитки MessageLoop
t.interrupt();
// Може зайняти певний час
// -- безкінечне очікування
t.join();
}
}
threadMessage("Кінець!");
}
}
При виконанні коду, без задання аргументу, скоріш за все нитка виконання MessageLoop успішно завершить свою роботу, а за нею вже і основна нитка. Якщо ж змінити значення змінної patience на менше, або через аргумент, або прямо в коді, то ми отримаємо перед завершенння програми повідомлення "Виконання задачі не завершене!".
Ось результат з успішним завершення виконання MessageLoop:
main: Старт MessageLoop thread main: Чекаю допоки MessageLoop thread не закінчить main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... Thread-0: У діброві - дуби, main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... Thread-0: Під дубами - гриби, main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... Thread-0: Трава - між грибами, main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... main: Все ще очікую... Thread-0: Хмарки - над дубами. main: Кінець!
Інтерференція ниток
ред.Синхронізація
ред.(Ще не написано)
Джерела інформації
ред.(Даний розділ незавершений і потребує доповнення)