В далёком снежном королевстве, где сугробы сияют, словно бриллианты, а звёзды нашёптывают сказки, Санта готовился к самой волшебной ночи года. Его фабрика гудела, как гигантский музыкальный инструмент: эльфы бегали с коробками, игрушки сверкали разноцветными красками, а колокольчики звенели с каждым новым готовым подарком.
Но волшебство оказалось под угрозой.
"Слишком много игрушек! Лента переполнена!" — жаловался главный эльф-производитель.
"Мы не успеваем упаковывать!" — вторил ему эльф-упаковщик.
Санта задумался: "Как помочь моим эльфам? Они стараются изо всех сил, но без магии не обойтись".
Первая магия: Синхронизация
Санта обратился к заклинанию, известному как 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(); // Уведомляем производителей о свободном месте
}
}
}
Магические свойства wait
и notifyAll
:
wait()
: Усыпляет поток, пока условие (например, пустая лента) не станет ложным.
notifyAll()
: Будит всех ожидающих потоков, чтобы они проверили, могут ли продолжить работу.
Вторая магия: BlockingQueue
Санта понял, что эльфы часто забывали вызывать notifyAll, и это создавало задержки. Тогда он применил магический инструмент — BlockingQueue, который сам заботился о потоках.
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);
}
}
Третья магия: Executors и пул потоков
Чтобы эльфы не уставали, Санта создал магический инструмент — пул потоков. Теперь каждый эльф выполнял свою задачу, и их количество автоматически регулировалось.
"Эльфы больше не будут бегать друг за другом, всё работает плавно," — радовался Санта.
Пример кода:
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(); // Завершаем работу после выполнения всех задач
}
}
Магические свойства Executors:
newFixedThreadPool(n)
: Создаёт пул из n потоков, которые будут повторно использоваться.
submit(task)
: Запускает задачу в одном из потоков.
shutdown()
: Завершает выполнение после завершения всех задач.
Четвёртая магия: CompletableFuture
На последнем этапе Санта добавил немного дополнительного волшебства: теперь эльфы работали асинхронно, что ускоряло процесс.
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();
}
});
}
}
Магические свойства CompletableFuture:
Асинхронность: задачи выполняются в фоновом потоке, не блокируя основной поток.
Простота использования: не нужно вручную управлять потоками.
Новый год спасён!
Когда последние игрушки были упакованы, Санта с радостью вздохнул. "Эльфы, вы проделали невероятную работу. Магия многопоточности сделала нас не только быстрее, но и счастливее!"
И теперь каждую ночь под Новый год Санта рассказывает своим помощникам сказку о том, как многопоточность спасла праздник.
✨ С Новым годом, друзья! 🎄
Top comments (0)