DEV Community

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

Posted on

1 1 1 1 1

Праздничные гонки Санты: Волшебство многопоточности 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

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay