Практические паттерны и оптимизация в Go vs Java | Concurrency часть 3

В этой части мы рассмотрим практические паттерны параллельной обработки задач: worker pool, pipeline pattern и схемы сборки результатов. Эти паттерны помогают повысить производительность и избегать deadlocks.

1. Worker Pool

Worker pool позволяет ограничить количество одновременно работающих goroutine, что эффективно для контроля ресурсов.


// Go: Worker Pool
jobs := make(chan int, 10)
results := make(chan int, 10)

for w := 1; w <= 3; w++ {
    go func(id int) {
        for j := range jobs {
            results <- j*2
        }
    }(w)
}

// Отправляем задачи
for j := 1; j <= 5; j++ {
    jobs <- j
}
close(jobs)

// Получаем результаты
for a := 1; a <= 5; a++ {
    fmt.Println(<-results)
}

// Java: Worker Pool
ExecutorService executor = Executors.newFixedThreadPool(3);

try {
    List<Future<Integer>> futures = new ArrayList<>();

    for (int j = 1; j <= 5; j++) {
        int task = j;
        futures.add(executor.submit(() -> task * 2));
    }

    for (Future<Integer> f : futures) {
        System.out.println(f.get());
    }

} finally {
    executor.shutdown();
}
Worker pool полезен, когда нужно ограничить количество одновременно выполняемых задач и контролировать нагрузку на систему.

2. Pipeline Pattern

Pipeline pattern — организация последовательной обработки данных через цепочку каналов (stage-by-stage). Позволяет разделять задачи на этапы и эффективно использовать goroutine.


// Go: Pipeline
source := make(chan int, 5)
stage1 := make(chan int, 5)
stage2 := make(chan int, 5)

// Stage 1
go func() {
    for n := range source {
        stage1 <- n * 2
    }
    close(stage1)
}()

// Stage 2
go func() {
    for n := range stage1 {
        stage2 <- n + 1
    }
    close(stage2)
}()

// Отправка данных
for i := 1; i <= 5; i++ {
    source <- i
}
close(source)

// Получение результатов
for r := range stage2 {
    fmt.Println(r)
}

// Java: Pipeline с CompletableFuture
List<CompletableFuture<Integer>> pipeline = new ArrayList<>();

for (int i = 1; i <= 5; i++) {
    pipeline.add(
        CompletableFuture.supplyAsync(() -> i)
            .thenApply(n -> n * 2)
            .thenApply(n -> n + 1)
    );
}

for (CompletableFuture<Integer> f : pipeline) {
    System.out.println(f.get());
}
Pipeline помогает разделять задачи на этапы и снижает вероятность блокировок. Особенно полезно для потоковой обработки данных.

3. Мини‑схема: «Параллельная обработка задач + сбор результатов»

Визуально паттерны worker pool + fan-in можно показать так:

        jobs
         │
         ▼
   ┌───────────┐
   │  Worker 1 │
   └───────────┘
         │
   ┌───────────┐
   │  Worker 2 │
   └───────────┘
         │
   ┌───────────┐
   │  Worker 3 │
   └───────────┘
         │
         ▼
      results

4. Лучшие практики

  • Ограничивайте количество одновременно работающих goroutine (worker pool).
  • Используйте buffered каналы для избежания блокировки при fan-in/fan-out.
  • Всегда закрывайте каналы, когда они больше не нужны.
  • Мониторьте гонки и deadlocks с помощью go run -race.
  • Разделяйте задачи на стадии через pipeline для читаемости и безопасности.
Планирование worker pool и pipeline помогает безопасно масштабировать обработку задач и повышает производительность без риска deadlocks.

Итог

В этой статье мы рассмотрели практические паттерны параллельной обработки задач в Go: worker pool, pipeline pattern и сбор результатов через fan-in. Эти паттерны помогают эффективно использовать ресурсы, избегать блокировок и упрощают масштабирование.

Для Java-разработчика это похоже на использование фиксированных пулов потоков, CompletableFuture и последовательной обработки через stage-by-stage. Освоение этих паттернов позволит создавать высокопроизводительные и безопасные многопоточные приложения.


Всего лайков:0
Мой канал в социальных сетях
Отправляя email, вы принимаете условия политики конфиденциальности

Полезные статьи:

Асинхронность и реактивность в Java: CompletableFuture, Flow и Virtual Threads
В современном Java-разработке есть три основных подхода к асинхронности и параллельности: CompletableFuture — для одиночных асинхронных задач. Flow / Reactive Streams — для потоков данных с контролем...
Основы параллельности в Go для Java-разработчиков | Сoncurrency часть 1
Если вы Java-разработчик, привыкший к потокам и ExecutorService, Go предлагает более лёгкий и удобный подход к параллельной обработке — goroutine и каналы. В этой статье мы разберём ключевые концепции...
Channel direction и select patterns в Go vs Java | Паттерны, идиомы и лучшие практики Go
← Связанные статьи: Context, propagation и cancellation patterns в Go vs Java | Паттерны, идиомы и лучшие практики Go 1. Channel direction — направление каналов В Go каналы могут быть односторонн...

Новые статьи:

Внутреннее устройство Garbage Collector: Go ↔ Java
В этой статье мы подробно разберём работу сборщика мусора (Garbage Collector, GC) в Go и Java, рассмотрим ключевые внутренние механизмы: concurrent mark &amp; sweep, mutator vs collector, tricolor mar...
Memory, Runtime и Allocator: Сравнение Go и Java для разработчиков
В этой статье мы разберём ключевые аспекты работы с памятью, runtime и механизмами аллокации объектов в Go и Java. Мы сфокусируемся на различиях подходов к управлению памятью, работе со стеком и кучей...
Atomic vs Mutex, Blocking vs Non‑Blocking, Read/Write Splitting (RWMutex), Logging | Concurrency Patterns и Best Practices  часть 5 | Go ↔ Java
В этой статье мы разберём ключевые подходы к работе с параллелизмом и синхронизацией в Go и Java. Мы сравним, как одни и те же задачи решаются на этих языках, покажем идиомы, паттерны и лучшие практик...
Fullscreen image