Добавить в корзинуПозвонить
Найти в Дзене
Java

🧩 Задача «Три счётчика» (с подвохом

🧩 Задача «Три счётчика» (с подвохом) Условие Дан класс Counter с полем int value и методом increment(). Нужно запустить три параллельных потока, каждый увеличивает счётчик ровно 1 000 000 раз. В финале программа должна вывести Counter value = 3000000 Нельзя использовать synchronized, ReentrantLock, Atomic*, LongAdder, VarHandle. Допустимы любые потоки (обычные или виртуальные) и любая коллекция из стандартной библиотеки Java 19+. Тип поля менять нельзя — только int. ⚡️ В чем здесь подвох? Операция value++ не атомарна: «прочитать → увеличить → записать». Без привычных примитивов придётся найти альтернативный путь синхронизации. 💡 Решение через message queue (Actor‑подход) Создаём очередь команд BlockingQueue<Runnable>. Поднимаем один служебный поток servo, который единственный обращается к Counter.value. Три рабочих потока кладут в очередь лямбду counter::increment. ```java import java.util.concurrent.*; final class Counter { int value; void increment() { value++; } } pub

🧩 Задача «Три счётчика» (с подвохом)

Условие

Дан класс Counter с полем int value и методом increment().

Нужно запустить три параллельных потока, каждый увеличивает счётчик ровно 1 000 000 раз.

В финале программа должна вывести

Counter value = 3000000

Нельзя использовать synchronized, ReentrantLock, Atomic*, LongAdder, VarHandle.

Допустимы любые потоки (обычные или виртуальные) и любая коллекция из стандартной библиотеки Java 19+.

Тип поля менять нельзя — только int.

⚡️ В чем здесь подвох?

Операция value++ не атомарна: «прочитать → увеличить → записать».

Без привычных примитивов придётся найти альтернативный путь синхронизации.

💡 Решение через message queue (Actor‑подход)

Создаём очередь команд BlockingQueue<Runnable>.

Поднимаем один служебный поток servo, который единственный обращается к Counter.value.

Три рабочих потока кладут в очередь лямбду counter::increment.

```java

import java.util.concurrent.*;

final class Counter {

int value;

void increment() { value++; }

}

public class ThreeCountersDemo {

public static void main(String[] args) throws InterruptedException {

Counter counter = new Counter();

BlockingQueue<Runnable> q = new LinkedBlockingQueue<>();

// 1️⃣ Сервисный поток: изменяет value

Thread servo = Thread.startVirtualThread(() -> {

try { while (true) q.take().run(); }

catch (InterruptedException ignored) {}

});

// 2️⃣ Три рабочих потока — по миллиону инкрементов

Runnable worker = () -> {

for (int i = 0; i < 1_000_000; i++)

q.add(counter::increment);

};

Thread.ofVirtual().start(worker);

Thread.ofVirtual().start(worker);

Thread.ofVirtual().start(worker);

// 3️⃣ Ждём опустошения очереди и выключаем сервис

while (!q.isEmpty()) Thread.sleep(10);

servo.interrupt();

servo.join();

System.out.println("Counter value = " + counter.value);

}

}```

✅ Почему это работает

- Value модифицирует только поток servo.

- Очереди java.util.concurrent не были запрещены.

- Параллельность: виртуальные потоки лёгкие (~2 КБ стек), можно масштабировать.

@javarush