Освоюємо 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: Кінець!
Інтерференція ниток
[ред.]Синхронізація
[ред.](Ще не написано)
Джерела інформації
[ред.](Даний розділ незавершений і потребує доповнення)