Взаємодія потоків. Методи wait та notify. Семафори

Прокрутити вниз

Взаємодія потоків. Методи wait та notify. Семафори – java курс

Взаємодія між потоками в багатопоточних програмах важлива, оскільки потоки можуть залежати один від одного. Наприклад, один потік може чекати, поки інший виконає якусь дію. У Java для організації взаємодії між потоками використовуються методи `wait()`, `notify()`, `notifyAll()` і семафори.

Методи `wait()` і `notify()`

Ці методи дозволяють організовувати взаємодію між потоками через **монітор** об’єкта. Кожен об’єкт в Java має власний монітор, і методи `wait()`, `notify()` і `notifyAll()` дозволяють одному потоку призупинити виконання, поки інший не повідомить, що можна продовжувати.

Як працюють ці методи:

  • wait(): Викликається потоком на об’єкті, змушуючи цей потік чекати, поки інший потік не викличе метод `notify()` або `notifyAll()` на цьому ж об’єкті.
  • notify(): Викликається потоком для пробудження одного з потоків, що чекає на цьому об’єкті.
  • notifyAll(): Пробуджує всі потоки, що чекають на об’єкті.

Методи `wait()`, `notify()`, `notifyAll()` можуть бути викликані тільки всередині **синхронізованого блоку** або методу.

Приклад:

class Message {
private String message;

public synchronized void send(String msg) {
this.message = msg;
notify(); // Повідомляє потік, що дані готові
}

public synchronized String receive() throws InterruptedException {
wait(); // Чекає, поки інший потік не відправить повідомлення
return message;
}
}

public class Main {
public static void main(String[] args) {
Message message = new Message();

// Потік для отримання повідомлення
Thread receiver = new Thread(() -> {
try {
String receivedMessage = message.receive();
System.out.println(“Отримано повідомлення: ” + receivedMessage);
} catch (InterruptedException e) {
e.printStackTrace();
}
});

// Потік для відправки повідомлення
Thread sender = new Thread(() -> {
try {
Thread.sleep(1000); // Затримка перед відправкою
message.send(“Привіт, це потік відправника!”);
} catch (InterruptedException e) {
e.printStackTrace();
}
});

receiver.start();
sender.start();
}
}

У цьому прикладі потік **receiver** чекає за допомогою `wait()`, поки **sender** не викличе метод `notify()`, щоб повідомити про наявність даних для обробки.

Основні моменти при використанні `wait()` і `notify()`:

  1. Синхронізація: Методи `wait()`, `notify()` і `notifyAll()` можуть бути викликані тільки всередині синхронізованого блоку або методу, інакше виникає виключення `IllegalMonitorStateException`.
  2. wait(): Зупиняє потік і передає монітор об’єкта, дозволяючи іншим потокам працювати з цим об’єктом.
  3. notify(): Пробуджує один із потоків, який чекає на моніторі об’єкта, але не гарантує, який саме потік буде пробуджений.
  4. notifyAll(): Пробуджує всі потоки, що чекають на моніторі об’єкта, але тільки один з них отримає управління після завершення синхронізованого блоку.

Семафори

Семафор (semaphore) — це механізм синхронізації, який обмежує кількість потоків, що можуть одночасно мати доступ до певного ресурсу. На відміну від `synchronized`, семафор дозволяє обмежити не тільки один потік, але й декілька (в залежності від кількості дозволених потоків).

У Java семафори представлені класом `Semaphore`, який знаходиться в пакеті `java.util.concurrent`.

Основні методи класу `Semaphore`:

  • `acquire()`: Зменшує лічильник дозволених потоків на 1. Якщо лічильник досяг нуля, потік блокується, поки не звільниться дозвіл.
  • `release()`: Збільшує лічильник дозволених потоків, дозволяючи новому потоку отримати доступ до ресурсу.

Приклад використання семафора:

import java.util.concurrent.Semaphore;

class SharedResource {
private Semaphore semaphore = new Semaphore(1); // Дозволяє одному потоку доступ

public void accessResource(String threadName) {
try {
semaphore.acquire(); // Отримує дозвіл на доступ до ресурсу
System.out.println(threadName + ” отримав доступ до ресурсу.”);
Thread.sleep(2000); // Виконання дії
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(threadName + ” звільняє ресурс.”);
semaphore.release(); // Звільняє дозвіл
}
}
}

public class Main {
public static void main(String[] args) {
SharedResource sharedResource = new SharedResource();

Thread t1 = new Thread(() -> sharedResource.accessResource(“Потік 1”));
Thread t2 = new Thread(() -> sharedResource.accessResource(“Потік 2”));
Thread t3 = new Thread(() -> sharedResource.accessResource(“Потік 3”));

t1.start();
t2.start();
t3.start();
}
}

У цьому прикладі семафор з кількістю дозволів, рівною 1, дозволяє одночасний доступ тільки одному потоку до ресурсу. Інші потоки чекають, поки доступ не буде звільнено.

Типи семафорів:

  • Бінарний семафор (Semaphore з кількістю дозволів = 1) працює подібно до блокування або мютексу, дозволяючи доступ лише одному потоку.
  • Підрахунковий семафор дозволяє кільком потокам одночасно отримувати доступ до ресурсу, в залежності від кількості дозволів.

Висновок

  • Методи `wait()` і `notify()` дозволяють потокам взаємодіяти через монітор об’єкта, передаючи контроль між потоками.
  • Семафори використовуються для обмеження кількості потоків, які одночасно мають доступ до ресурсу.
  • Використання `synchronized`, `wait()`, `notify()`, семафорів дозволяє ефективно синхронізувати потоки та запобігати виникненню конфліктів доступу до спільних ресурсів.

Взаємодія потоків. Методи wait та notify. Семафори

Insert math as
Block
Inline
Additional settings
Formula color
Text color
#333333
Type math using LaTeX
Preview
\({}\)
Nothing to preview
Insert