# Concurrencia en Quarkus

Vídeo sobre la historia de los hilos hasta llegar a los hilos virtuales.

<iframe width="560" height="315" src="https://www.youtube.com/embed/6wbZ2FBz5dA?si=vP3VvQw3dly7JEyI"></iframe>

Vídeo de la demo explicando las pruebas de concurrencia según cada enfoque:

<iframe width="560" height="315" src="https://www.youtube.com/embed/ctbMLeOVA8g?si=Ca3-ZoTFwZP0xC38"></iframe>

## **📚 Introducción**

Este proyecto es una demostración educativa construido por [Willem Jan Glerum](https://www.linkedin.com/in/wjglerum/) compara **4 enfoques diferentes de concurrencia** en Quarkus para implementar una API REST que simula un barista preparando bebidas y guardándolas en base de datos.

**Github Repository:** [https://github.com/joedayz/quarkus-virtual-threads](https://github.com/joedayz/quarkus-virtual-threads)

**Requisitos:**

* Java 26+
    
* Maven 3.9.x+
    

### **Objetivo del Proyecto**

Simular un barista que:

* Prepara café (simula una operación que tarda 3 segundos)
    
* Guarda el resultado en base de datos
    
* Maneja múltiples pedidos de forma concurrente
    

Cada módulo implementa la **misma funcionalidad** pero con un **estilo de concurrencia diferente**, permitiendo comparar sus ventajas y desventajas.

---

## **🏗️ Arquitectura del Proyecto**

El proyecto está organizado en 4 módulos principales:

```go
src/main/java/nl/wjglerum/
├── _01_blocking/      # Enfoque tradicional bloqueante
├── _02_reactive/      # Programación reactiva
├── _03_virtual/       # Virtual Threads
└── _04_structured/    # Structured Concurrency
```

Cada módulo contiene:

* **Resource**: Endpoints REST (`*BeverageResource.java`)
    
* **Bartender**: Lógica de negocio que simula preparar bebidas (`*Bartender.java`)
    
* **Beverage**: Modelo de datos (`*Beverage.java`)
    
* **Repository**: Persistencia en base de datos (`*BeverageRepository.java`)
    

---

## **📦 Módulo 1: Blocking (Bloqueante)**

### **Descripción**

Enfoque **tradicional** usando threads de plataforma del sistema operativo con código bloqueante.

### **Características Técnicas**

* **Threads**: Threads de plataforma (OS threads)
    
* **Base de datos**: Hibernate ORM (bloqueante)
    
* **Código**: Estilo imperativo tradicional
    
* **Anotación**: `@Transactional`
    

### **Cómo Funciona**

```java
public BlockingBeverage get() {
    Thread.sleep(3_000);  // Bloquea el thread actual
    return new BlockingBeverage("Blocking coffee");
}
```

* Cada request consume **un thread del pool** (limitado, típicamente ~200 threads)
    
* Si llegan muchas requests simultáneas, **se agotan los threads**
    
* El servidor puede dejar de responder cuando se satura
    

### **Endpoints Disponibles**

| Endpoint | Descripción | Tiempo Estimado |
| --- | --- | --- |
| `GET /beverage/blocking` | Una bebida | ~3 segundos |
| `GET /beverage/blocking/sequential` | Tres bebidas secuencialmente | ~9 segundos |
| `GET /beverage/blocking/parallel` | Tres bebidas en paralelo | ~3 segundos |

### **Ejemplo de Código: Paralelo**

```java
@GET
@Path("/parallel")
public List<BlockingBeverage> getBeveragesParallel() {
    var beverage1 = executor.submit(bartender::get);
    var beverage2 = executor.submit(bartender::get);
    var beverage3 = executor.submit(bartender::get);
    var beverages = List.of(
        beverage1.get(), 
        beverage2.get(), 
        beverage3.get()
    );
    repository.save(beverages);
    return beverages;
}
```

### **Ventajas**

✅ Código simple y fácil de entender  
✅ Estilo imperativo tradicional  
✅ Fácil de depurar

### **Desventajas**

❌ Limitado por el número de threads de plataforma  
❌ No escala bien con muchas requests concurrentes  
❌ Puede agotar recursos del servidor

### **Diagrama de Flujo**

```go
Request 1 ──┐
Request 2 ──┤
Request 3 ──┼──> Thread Pool (200 threads) ──> ❌ Se agota
Request 4 ──┤
...         ┘
```

---

## **⚡ Módulo 2: Reactive (Reactivo)**

### **Descripción**

Programación **reactiva** usando Mutiny y Hibernate Reactive. No bloquea threads, usa un modelo de eventos.

### **Características Técnicas**

* **Threads**: Event loop (pocos threads)
    
* **Base de datos**: Hibernate Reactive (no bloqueante)
    
* **Código**: Estilo reactivo con `Uni<T>`
    
* **Anotación**: `@WithTransaction`
    

### **Cómo Funciona**

```java
public Uni<ReactiveBeverage> get() {
    return Uni.createFrom()
        .item(new ReactiveBeverage("Reactive coffee"))
        .onItem().delayIt()
        .by(Duration.ofSeconds(3));  // No bloquea el thread
}
```

* Usa `Uni` (tipo reactivo de Mutiny)
    
* **No bloquea threads**; usa un modelo de eventos
    
* Hibernate Reactive ejecuta operaciones de forma no bloqueante
    
* `delayIt()` simula la espera sin bloquear
    

### **Endpoints Disponibles**

| Endpoint | Descripción | Tiempo Estimado |
| --- | --- | --- |
| `GET /beverage/reactive` | Una bebida reactiva | ~3 segundos |
| `GET /beverage/reactive/sequential` | Tres bebidas secuencialmente | ~9 segundos |
| `GET /beverage/reactive/parallel` | Tres bebidas en paralelo | ~3 segundos |

### **Ejemplo de Código: Paralelo**

```java
@GET
@Path("/parallel")
public Uni<List<ReactiveBeverage>> getBeveragesParallel() {
    var beverage1 = bartender.get();
    var beverage2 = bartender.get();
    var beverage3 = bartender.get();
    return Uni.join()
        .all(beverage1, beverage2, beverage3)
        .andCollectFailures()
        .onItem()
        .call(beverages -> repository.save(beverages));
}
```

### **Ventajas**

✅ Escala muy bien con muchas requests concurrentes  
✅ Usa pocos threads (event loop)  
✅ Eficiente en recursos

### **Desventajas**

❌ Código más complejo (callbacks, composición)  
❌ Curva de aprendizaje más alta  
❌ Estilo de programación diferente al tradicional

### **Diagrama de Flujo**

```go
Request 1 ──┐
Request 2 ──┤
Request 3 ──┼──> Event Loop (pocos threads) ──> ✅ Escala bien
Request 4 ──┤
...         ┘
```

---

## **🧵 Módulo 3: Virtual Threads (Hilos Virtuales)**

### **Descripción**

Usa **Virtual Threads** (Java 21+) con código bloqueante tradicional, pero sin bloquear threads de plataforma.

### **Características Técnicas**

* **Threads**: Virtual Threads (gestionados por la JVM)
    
* **Base de datos**: Hibernate ORM (bloqueante, pero sobre virtual threads)
    
* **Código**: Estilo imperativo tradicional
    
* **Anotación**: `@RunOnVirtualThread` + `@Transactional`
    

### **Cómo Funciona**

```java
@RunOnVirtualThread
public VirtualBeverage get() {
    Thread.sleep(3_000);  // Bloquea el virtual thread, no el OS thread
    return new VirtualBeverage("Virtual coffee");
}
```

* Usa `@RunOnVirtualThread` para ejecutar en virtual threads
    
* Código bloqueante (`Thread.sleep`) pero **sin bloquear threads de plataforma**
    
* Hibernate ORM bloqueante, pero sobre virtual threads
    
* Virtual threads son **ligeros** (millones posibles)
    

### **Endpoints Disponibles**

| Endpoint | Descripción | Tiempo Estimado |
| --- | --- | --- |
| `GET /beverage/virtual` | Una bebida con virtual thread | ~3 segundos |
| `GET /beverage/virtual/sequential` | Tres bebidas secuencialmente | ~9 segundos |
| `GET /beverage/virtual/parallel` | Tres bebidas en paralelo | ~3 segundos |
| `GET /beverage/virtual/custom` | Tres bebidas con executor personalizado | ~3 segundos |

### **Ejemplo de Código: Paralelo**

```java
@Inject
@VirtualThreads
ExecutorService executor;

@GET
@Path("/parallel")
public List<VirtualBeverage> getBeveragesParallel() {
    var beverage1 = executor.submit(bartender::get);
    var beverage2 = executor.submit(bartender::get);
    var beverage3 = executor.submit(bartender::get);
    var beverages = List.of(
        beverage1.get(), 
        beverage2.get(), 
        beverage3.get()
    );
    repository.save(beverages);
    return beverages;
}
```

### **Ejemplo de Código: Custom Executor**

```java
@GET
@Path("/custom")
public List<VirtualBeverage> getBeveragesCustom() {
    var currentThread = Thread.currentThread().getName();
    var threadFactory = Thread.ofVirtual()
        .name(currentThread + "-virtual-beverage-", 0)
        .factory();
    try(var executor = Executors.newThreadPerTaskExecutor(threadFactory)) {
        var beverage1 = executor.submit(bartender::get);
        var beverage2 = executor.submit(bartender::get);
        var beverage3 = executor.submit(bartender::get);
        var beverages = List.of(
            beverage1.get(), 
            beverage2.get(), 
            beverage3.get()
        );
        repository.save(beverages);
        return beverages;
    }
}
```

### **Ventajas**

✅ Código bloqueante simple (como Blocking)  
✅ Escalabilidad de programación reactiva  
✅ Mejor de ambos mundos  
✅ Compatible con código existente

### **Desventajas**

❌ Requiere Java 21+  
❌ Virtual threads aún son relativamente nuevos

### **Diagrama de Flujo**

```go
Request 1 ──┐
Request 2 ──┤
Request 3 ──┼──> Virtual Threads (millones) ──> ✅ Escala bien
Request 4 ──┤
...         ┘
```

---

## **🎯 Módulo 4: Structured Concurrency (Concurrencia Estructurada)**

### **Descripción**

Usa **Structured Concurrency** (Java 21+) con virtual threads para gestionar tareas concurrentes de forma estructurada.

### **Características Técnicas**

* **Threads**: Virtual Threads con Structured Concurrency
    
* **Base de datos**: Hibernate ORM
    
* **Código**: Estilo imperativo con `StructuredTaskScope`
    
* **Anotación**: `@RunOnVirtualThread` + `@Transactional`
    

### **Cómo Funciona**

```java
@RunOnVirtualThread
public List<StructuredBeverage> getBeveragesSimple() throws InterruptedException {
    try (var scope = StructuredTaskScope.open()) {
        var beverage1 = scope.fork(() -> bartender.get());
        var beverage2 = scope.fork(() -> bartender.get());
        var beverage3 = scope.fork(() -> bartender.get());
        scope.join();  // Espera a que todas terminen
        var beverages = List.of(
            beverage1.get(), 
            beverage2.get(), 
            beverage3.get()
        );
        repository.save(beverages);
        return beverages;
    }  // Garantiza que todas las tareas terminan aquí
}
```

* Usa `StructuredTaskScope` para gestionar tareas concurrentes
    
* **Garantiza** que todas las tareas hijas terminan antes de que el scope cierre
    
* Evita "thread leaks" y facilita el manejo de errores
    
* El scope se cierra automáticamente con `try-with-resources`
    

### **Endpoints Disponibles**

| Endpoint | Descripción | Tiempo Estimado |
| --- | --- | --- |
| `GET /beverage/structured/simple` | Tres bebidas con StructuredTaskScope básico | ~3 segundos |
| `GET /beverage/structured/custom` | Tres bebidas con Joiner personalizado | ~3 segundos |

### **Ejemplo de Código: Simple**

```java
@GET
@Path("/simple")
public List<StructuredBeverage> getBeveragesSimple() throws InterruptedException {
    try (var scope = StructuredTaskScope.open()) {
        var beverage1 = scope.fork(() -> bartender.get());
        var beverage2 = scope.fork(() -> bartender.get());
        var beverage3 = scope.fork(() -> bartender.get());
        scope.join();
        var beverages = List.of(
            beverage1.get(), 
            beverage2.get(), 
            beverage3.get()
        );
        repository.save(beverages);
        return beverages;
    }
}
```

### **Ejemplo de Código: Custom Joiner**

```java
@GET
@Path("/custom")
public List<StructuredBeverage> getBeveragesCustom() throws InterruptedException {
    var joiner = StructuredTaskScope.Joiner.<StructuredBeverage>allSuccessfulOrThrow();
    var currentThread = Thread.currentThread().getName();
    var threadFactory = Thread.ofVirtual()
        .name(currentThread + "-structured-beverage-", 0)
        .factory();
    try (var scope = StructuredTaskScope.open(joiner, cf -> 
        cf.withThreadFactory(threadFactory))) {
        scope.fork(bartender::get);
        scope.fork(bartender::get);
        scope.fork(bartender::get);
        var beverages = scope.join();  // Retorna List directamente
        repository.save(beverages);
        return beverages;
    }
}
```

### **Ventajas**

✅ Mejor control del ciclo de vida de tareas concurrentes  
✅ Manejo de errores más robusto  
✅ Evita "thread leaks"  
✅ Garantiza que todas las tareas terminan correctamente  
✅ Código más seguro y predecible

### **Desventajas**

❌ Requiere Java 21+  
❌ API aún en preview (puede cambiar)

---

## **🔍 Comparación de Enfoques**

### **Tabla Comparativa**

| Característica | Blocking | Reactive | Virtual | Structured |
| --- | --- | --- | --- | --- |
| **Código** | Simple | Complejo | Simple | Simple |
| **Escalabilidad** | ❌ Limitada | ✅ Excelente | ✅ Excelente | ✅ Excelente |
| **Threads** | OS Threads (~200) | Event Loop (pocos) | Virtual (millones) | Virtual (millones) |
| **Base de Datos** | Hibernate ORM | Hibernate Reactive | Hibernate ORM | Hibernate ORM |
| **Estilo** | Imperativo | Reactivo | Imperativo | Imperativo |
| **Java Version** | 8+ | 8+ | 21+ | 21+ |
| **Curva Aprendizaje** | Baja | Alta | Media | Media |

### **Cuándo Usar Cada Enfoque**

#### **🟦 Blocking**

* ✅ Aplicaciones pequeñas con bajo tráfico
    
* ✅ Equipos sin experiencia en programación reactiva
    
* ✅ Prototipos rápidos
    
* ❌ No usar en aplicaciones de alto tráfico
    

#### **⚡ Reactive**

* ✅ Aplicaciones de alto rendimiento
    
* ✅ Equipos con experiencia en programación reactiva
    
* ✅ Cuando necesitas control fino sobre el flujo asíncrono
    
* ❌ No usar si el equipo no está familiarizado con el estilo reactivo
    

#### **🧵 Virtual Threads**

* ✅ **Recomendado para la mayoría de casos**
    
* ✅ Migración fácil desde código bloqueante
    
* ✅ Aplicaciones de alto tráfico con código simple
    
* ✅ Mejor balance entre simplicidad y escalabilidad
    

#### **🎯 Structured Concurrency**

* ✅ Cuando necesitas garantías sobre el ciclo de vida de tareas
    
* ✅ Operaciones complejas con múltiples subtareas
    
* ✅ Cuando quieres evitar "thread leaks"
    
* ✅ Aplicaciones críticas donde la seguridad es importante
    

---

## **🎓 Conceptos Clave**

### **1\. Thread.sleep(3000)**

Simula una operación lenta (preparar café). En producción sería una llamada a API externa, consulta a base de datos, etc.

### **2\. Hibernate ORM vs Hibernate Reactive**

**Hibernate ORM (Bloqueante)**:

* Bloquea el thread mientras espera la respuesta de la base de datos
    
* Usado en: Blocking, Virtual, Structured
    

**Hibernate Reactive (No Bloqueante)**:

* No bloquea el thread, usa callbacks
    
* Usado en: Reactive
    

### **3\. Virtual Threads**

Threads **ligeros** gestionados por la JVM:

* Millones de virtual threads pueden ejecutarse sobre pocos threads de plataforma
    
* Cuando un virtual thread se bloquea, el thread de plataforma se libera para otro virtual thread
    
* Introducido en Java 21 (Project Loom)
    

### **4\. Structured Concurrency**

Paradigma que garantiza que:

* Las tareas concurrentes tienen un ciclo de vida bien definido
    
* Todas las tareas hijas terminan antes de que el scope padre termine
    
* Evita "thread leaks" y facilita el manejo de errores
    

### **5\. ManagedExecutor vs ExecutorService**

**ManagedExecutor**:

* Gestionado por Quarkus
    
* Usa threads de plataforma
    
* Usado en: Blocking
    

**ExecutorService con VirtualThreads**:

* Puede usar virtual threads
    
* Más flexible
    
* Usado en: Virtual, Structured
    

---

## **🚀 Cómo Probar los Ejercicios**

### **1\. Requisitos Previos**

* JDK 21+ (o JDK 26 early access según el README)
    
* Docker o Podman corriendo (para la base de datos)
    
* Maven instalado
    

### **2\. Ejecutar la Aplicación**

```bash
./mvnw quarkus:dev
```

La aplicación estará disponible en: `http://localhost:8080`

### **3\. Probar los Endpoints**

Usa el archivo `examples.http` que contiene todas las requests listas para probar:

```http
### GET Blocking Simple
GET http://localhost:8080/beverage/blocking

### GET Reactive Parallel
GET http://localhost:8080/beverage/reactive/parallel

### GET Virtual Parallel
GET http://localhost:8080/beverage/virtual/parallel

### GET Structured Simple
GET http://localhost:8080/beverage/structured/simple
```

### **4\. Observar los Logs**

Cada endpoint registra información útil:

* Qué tipo de bebida se está preparando
    
* Qué threads se están usando
    
* Tiempos de ejecución
    

### **5\. Comparar Rendimiento**

Prueba hacer múltiples requests simultáneas y observa:

* **Blocking**: Se satura rápidamente
    
* **Reactive**: Maneja muchas requests sin problemas
    
* **Virtual**: Maneja muchas requests sin problemas
    
* **Structured**: Maneja muchas requests sin problemas
    

---

## **📊 Ejemplo de Prueba**

### **Escenario: 100 Requests Simultáneas**

```go
Blocking:   ❌ Muchas requests fallan o timeout
Reactive:   ✅ Todas las requests completan exitosamente
Virtual:    ✅ Todas las requests completan exitosamente
Structured: ✅ Todas las requests completan exitosamente
```

### **Tiempos de Respuesta (3 bebidas paralelas)**

```go
Blocking Parallel:   ~3 segundos
Reactive Parallel:   ~3 segundos
Virtual Parallel:    ~3 segundos
Structured Simple:   ~3 segundos
```

---

## **🔗 Recursos Adicionales**

* [Quarkus Documentation](https://quarkus.io/)
    
* [Virtual Threads (Project Loom)](https://openjdk.org/projects/loom/)
    
* [Structured Concurrency](https://openjdk.org/jeps/453)
    
* [Mutiny Documentation](https://smallrye.io/smallrye-mutiny/)
    
* [Hibernate Reactive](https://hibernate.org/reactive/)
    

---

## **📝 Notas Finales**

Este proyecto es **educativo** y está diseñado para:

* Comparar diferentes enfoques de concurrencia
    
* Entender las ventajas y desventajas de cada uno
    
* Aprender cuándo usar cada enfoque
    

**No está diseñado para producción** sin las modificaciones y consideraciones apropiadas.

---

## **❓ Preguntas Frecuentes**

### **¿Cuál enfoque debería usar en mi proyecto?**

**Recomendación**: Virtual Threads (módulo 3) para la mayoría de casos, ya que ofrece:

* Código simple
    
* Excelente escalabilidad
    
* Fácil migración desde código bloqueante
    

### **¿Puedo mezclar enfoques?**

Sí, pero no es recomendado. Es mejor elegir un enfoque consistente para toda la aplicación.

### **¿Virtual Threads reemplazan a Reactive?**

No necesariamente. Cada uno tiene su lugar:

* **Virtual Threads**: Para código bloqueante que necesita escalar
    
* **Reactive**: Para aplicaciones que necesitan control fino sobre el flujo asíncrono
    

### **¿Qué pasa si uso Blocking en producción?**

Puede funcionar para aplicaciones pequeñas, pero se saturará con alto tráfico. Mejor migrar a Virtual Threads.

---

**¡Disfruta aprendiendo sobre concurrencia en Quarkus!** 🎉
