Потоки в Java
Потоки дозволяють програмі працювати ефективніше, виконуючи кілька завдань одночасно.
Потоки можна використовувати для виконання складних завдань у фоновому режимі без переривання основної програми.
Створення потоку
Є два способи створити потік.
Потік можна створити, розширивши клас Thread
та переозначивши його метод run()
:
public class Main extends Thread {
public void run() {
System.out.println("Цей код виконується в потоці");
}
}
Інший спосіб створити потік — реалізувати інтерфейс Runnable
:
public class Main implements Runnable {
public void run() {
System.out.println("Цей код виконується в потоці");
}
}
Запуск потоків
Якщо клас розширює клас Thread
, потік можна запустити, створивши екземпляр класу та викликавши його метод start()
:
public class Main extends Thread {
public static void main(String[] args) {
Main thread = new Main();
thread.start();
System.out.println("Цей код знаходиться поза потоком");
}
public void run() {
System.out.println("Цей код виконується в потоці");
}
}
Якщо клас реалізує інтерфейс Runnable
, потік можна запустити, передавши екземпляр класу в конструктор об'єкта Thread
, а потім викликавши метод start()
потоку:
public class Main implements Runnable {
public static void main(String[] args) {
Main obj = new Main();
Thread thread = new Thread(obj);
thread.start();
System.out.println("Цей код знаходиться поза потоком");
}
public void run() {
System.out.println("Цей код виконується в потоці");
}
}
Різниця між "розширенням" і "реалізацією" потоків
Основна різниця полягає в тому, що коли клас розширює клас
Thread
, ви не можете розширювати інший клас, але реалізуючи інтерфейсRunnable
, можна також розширювати інший клас, наприклад: класMyClass extends OtherClass implements Runnable
.
Проблеми з паралельністю
Оскільки потоки виконуються одночасно з іншими частинами програми, немає способу визначити, в якому порядку буде виконуватися код. Коли потоки та основна програма читають і записують одні й ті ж змінні, значення можуть бути непередбачуваними. Проблеми, що виникають через це, називаються проблемами з паралельністю.
Приклад коду, де значення змінної amount є непередбачуваним:
public class Main extends Thread {
public static int amount = 0;
public static void main(String[] args) {
Main thread = new Main();
thread.start();
System.out.println(amount);
amount++;
System.out.println(amount);
}
public void run() {
amount++;
}
}
Щоб уникнути проблем з паралельністю, найкраще ділити якомога менше атрибутів між потоками. Якщо атрибути потрібно ділити, одним із можливих рішень є використання методу isAlive()
потоку для перевірки, чи завершив потік виконання, перед тим як використовувати будь-які атрибути, які потік може змінювати.
Використовуйте isAlive()
для запобігання проблемам з паралельністю:
public class Main extends Thread {
public static int amount = 0;
public static void main(String[] args) {
Main thread = new Main();
thread.start();
// Чекаємо, поки потік завершиться
while(thread.isAlive()) {
System.out.println("Чекаю...");
}
// Оновлюємо amount і виводимо його значення
System.out.println("Main: " + amount);
amount++;
System.out.println("Main: " + amount);
}
public void run() {
amount++;
}
}
Перекладено з: [Java — Threads](https://medium.com/@sithilasomaratne2010/java-threads-b10ef8aaf971)