DEV Community

Cover image for Праздничные гонки Санты: Волшебство многопоточности Java
Olga Lugacheva
Olga Lugacheva

Posted on

Праздничные гонки Санты: Волшебство многопоточности Java

В далёком снежном королевстве, где сугробы сияют, словно бриллианты, а звёзды нашёптывают сказки, Санта готовился к самой волшебной ночи года. Его фабрика гудела, как гигантский музыкальный инструмент: эльфы бегали с коробками, игрушки сверкали разноцветными красками, а колокольчики звенели с каждым новым готовым подарком.

Но волшебство оказалось под угрозой.

"Слишком много игрушек! Лента переполнена!" — жаловался главный эльф-производитель.
"Мы не успеваем упаковывать!" — вторил ему эльф-упаковщик.

Санта задумался: "Как помочь моим эльфам? Они стараются изо всех сил, но без магии не обойтись".

Первая магия: Синхронизация

Санта обратился к заклинанию, известному как synchronized. Оно создавало защитный барьер, чтобы эльфы не толкались и не путались при работе с общей лентой.

"Теперь только один из вас может класть или забирать игрушки одновременно", — объяснил Санта.

Вот как это выглядело:

class SantaFactory {
    private final Queue<String> conveyorBelt = new LinkedList<>();
    private final int MAX_CAPACITY = 10;

    public void produce(String toy) throws InterruptedException {
        synchronized (conveyorBelt) {
            while (conveyorBelt.size() == MAX_CAPACITY) {
                System.out.println("Производитель ждёт: лента заполнена!");
                conveyorBelt.wait(); // Производитель ждёт, пока появится место
            }
            conveyorBelt.add(toy);
            System.out.println("Игрушка произведена: " + toy);
            conveyorBelt.notifyAll(); // Уведомляем упаковщиков о новой игрушке
        }
    }

    public void consume() throws InterruptedException {
        synchronized (conveyorBelt) {
            while (conveyorBelt.isEmpty()) {
                System.out.println("Потребитель ждёт: игрушек нет!");
                conveyorBelt.wait(); // Упаковщик ждёт, пока появится игрушка
            }
            String toy = conveyorBelt.poll();
            System.out.println("Игрушка упакована: " + toy);
            conveyorBelt.notifyAll(); // Уведомляем производителей о свободном месте
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Магические свойства wait и notifyAll:

wait(): Усыпляет поток, пока условие (например, пустая лента) не станет ложным.
notifyAll(): Будит всех ожидающих потоков, чтобы они проверили, могут ли продолжить работу.

Вторая магия: BlockingQueue

Санта понял, что эльфы часто забывали вызывать notifyAll, и это создавало задержки. Тогда он применил магический инструмент — BlockingQueue, который сам заботился о потоках.

bq
BlockingQueue делает следующее:

Если лента заполнена, производитель автоматически ждёт.
Если лента пуста, упаковщик сам понимает, что нужно подождать.

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

class SantaFactoryBlocking {
    private final BlockingQueue<String> conveyorBelt = new ArrayBlockingQueue<>(10);

    public void produce(String toy) throws InterruptedException {
        conveyorBelt.put(toy); // Если лента заполнена, поток автоматически ждёт
        System.out.println("Игрушка произведена: " + toy);
    }

    public void consume() throws InterruptedException {
        String toy = conveyorBelt.take(); // Если лента пуста, поток автоматически ждёт
        System.out.println("Игрушка упакована: " + toy);
    }
}

Enter fullscreen mode Exit fullscreen mode

Третья магия: Executors и пул потоков

Чтобы эльфы не уставали, Санта создал магический инструмент — пул потоков. Теперь каждый эльф выполнял свою задачу, и их количество автоматически регулировалось.
elphs
"Эльфы больше не будут бегать друг за другом, всё работает плавно," — радовался Санта.

Пример кода:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SantaRaceWithExecutors {
    public static void main(String[] args) {
        SantaFactoryBlocking factory = new SantaFactoryBlocking();
        ExecutorService executor = Executors.newFixedThreadPool(4); // 4 эльфа

        // Запускаем 2 производителя
        for (int i = 1; i <= 2; i++) {
            executor.submit(() -> {
                try {
                    for (int j = 1; j <= 20; j++) {
                        factory.produce("Игрушка #" + j);
                        Thread.sleep(100); // Производство занимает время
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // Запускаем 2 упаковщика
        for (int i = 1; i <= 2; i++) {
            executor.submit(() -> {
                try {
                    for (int j = 1; j <= 20; j++) {
                        factory.consume();
                        Thread.sleep(150); // Упаковка занимает время
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown(); // Завершаем работу после выполнения всех задач
    }
}

Enter fullscreen mode Exit fullscreen mode

Магические свойства Executors:

newFixedThreadPool(n): Создаёт пул из n потоков, которые будут повторно использоваться.
submit(task): Запускает задачу в одном из потоков.
shutdown(): Завершает выполнение после завершения всех задач.

Четвёртая магия: CompletableFuture

compf
На последнем этапе Санта добавил немного дополнительного волшебства: теперь эльфы работали асинхронно, что ускоряло процесс.

import java.util.concurrent.CompletableFuture;

public class SantaRaceWithCompletableFuture {
    public static void main(String[] args) {
        SantaFactoryBlocking factory = new SantaFactoryBlocking();

        CompletableFuture.runAsync(() -> {
            try {
                for (int i = 1; i <= 20; i++) {
                    factory.produce("Игрушка #" + i);
                    Thread.sleep(100); // Производство занимает время
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        CompletableFuture.runAsync(() -> {
            try {
                for (int i = 1; i <= 20; i++) {
                    factory.consume();
                    Thread.sleep(150); // Упаковка занимает время
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }
}

Enter fullscreen mode Exit fullscreen mode

Магические свойства CompletableFuture:

Асинхронность: задачи выполняются в фоновом потоке, не блокируя основной поток.
Простота использования: не нужно вручную управлять потоками.

Новый год спасён!

Когда последние игрушки были упакованы, Санта с радостью вздохнул. "Эльфы, вы проделали невероятную работу. Магия многопоточности сделала нас не только быстрее, но и счастливее!"

И теперь каждую ночь под Новый год Санта рассказывает своим помощникам сказку о том, как многопоточность спасла праздник.

✨ С Новым годом, друзья! 🎄

sky

Top comments (0)