Расскажите о синхронизации между потоками. Для чего используют методы wait(), notify() – notifyAll(), join()?

Синхронизация между потоками в Java важна, когда несколько потоков имеют доступ к общим ресурсам и должны обеспечивать безопасное взаимодействие. Java предоставляет механизмы синхронизации, такие как synchronized блоки, мониторы, а также методы wait(), notify(), notifyAll() и join() для управления выполнением потоков.

1. wait(), notify(), notifyAll():

wait():

  • Метод wait() вызывается внутри synchronized блока и передает монитор (блокировку), позволяя другим потокам использовать его.
  • Поток, вызвавший wait(), переходит в состояние ожидания до тех пор, пока другой поток не вызовет notify() или notifyAll() на том же объекте.

notify():

  • Метод notify() будит один из потоков, ожидающих на том же объекте. Выбранный поток зависит от планировщика потоков.

notifyAll():

  • Метод notifyAll() будит все потоки, ожидающие на том же объекте.

Пример использования:

java

class SharedResource { boolean flag = false; synchronized void printNumbers() { while (!flag) { try { wait(); // Поток ожидает, пока другой поток не вызовет notify() } catch (InterruptedException e) { e.printStackTrace(); } } // Логика вывода чисел System.out.println("1"); System.out.println("2"); System.out.println("3"); flag = false; notify(); // Уведомление другого потока } synchronized void printLetters() { while (flag) { try { wait(); // Поток ожидает, пока другой поток не вызовет notify() } catch (InterruptedException e) { e.printStackTrace(); } } // Логика вывода букв System.out.println("A"); System.out.println("B"); System.out.println("C"); flag = true; notify(); // Уведомление другого потока } } public class Main { public static void main(String[] args) { SharedResource sharedResource = new SharedResource(); Thread t1 = new Thread(() -> { for (int i = 0; i < 5; i++) { sharedResource.printNumbers(); } }); Thread t2 = new Thread(() -> { for (int i = 0; i < 5; i++) { sharedResource.printLetters(); } }); t1.start(); t2.start(); } }

2. join():

Метод join() применяется для ожидания завершения другого потока. Текущий поток будет ожидать завершения потока, для которого вызывается join().

Пример использования:

java

public class JoinExample { public static void main(String[] args) { Thread t1 = new Thread(() -> { for (int i = 1; i <= 5; i++) { System.out.println("Thread 1 - Count: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(() -> { for (int i = 1; i <= 5; i++) { System.out.println("Thread 2 - Count: " + i); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } }); t1.start(); try { t1.join(); // Ожидание завершения t1 } catch (InterruptedException e) { e.printStackTrace(); } t2.start(); } }

В этом примере, после вызова t1.join(), программа будет ждать завершения потока t1, прежде чем продолжить выполнение.