Exchanger

Exchanger

Documentación sobre Exchanger en Java

Introducción a Exchanger

Exchanger<T> es una clase de Java perteneciente al paquete java.util.concurrent que proporciona un mecanismo de sincronización entre dos hilos, permitiendo que intercambien objetos de manera segura y eficiente. Este mecanismo es útil cuando se necesita coordinar el procesamiento de datos entre dos subprocesos antes de que continúen con sus respectivas tareas.

Funcionamiento de Exchanger

El Exchanger<T> actúa como un punto de sincronización donde dos hilos pueden llamar al método exchange(T obj), intercambiando los objetos que pasan como parámetro. El intercambio ocurre cuando ambos hilos han invocado el método exchange. Si un hilo llega primero, quedará bloqueado hasta que el otro hilo también invoque el método.

Principales Características

  1. Intercambio directo: Dos hilos intercambian datos directamente en un punto de sincronización.
  2. Sincronización obligatoria: Ambos hilos deben llegar al punto de intercambio para que la operación se complete.
  3. Bidireccionalidad: Cada hilo proporciona un objeto y recibe otro a cambio.
  4. Bloqueo hasta la sincronización: Si un hilo llega antes, se bloquea hasta que llegue el otro.
  5. Tiempo de espera opcional: Existe una versión sobrecargada del método exchange que permite especificar un tiempo de espera.

Implementación en Java

A continuación, se muestra un ejemplo de implementación de Exchanger en Java, donde dos hilos intercambian datos.

import java.util.concurrent.Exchanger;

class PrimerHilo implements Runnable {
    private Exchanger<Integer> exchanger;

    public PrimerHilo(Exchanger<Integer> exchanger) {
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        try {
            int datosParaEnviar = 10;
            System.out.println("Primer hilo enviando: " + datosParaEnviar);
            int datosRecibidos = exchanger.exchange(datosParaEnviar);
            System.out.println("Primer hilo recibió: " + datosRecibidos);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class SegundoHilo implements Runnable {
    private Exchanger<Integer> exchanger;

    public SegundoHilo(Exchanger<Integer> exchanger) {
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(3000);
            int datosParaEnviar = 20;
            System.out.println("Segundo hilo enviando: " + datosParaEnviar);
            int datosRecibidos = exchanger.exchange(datosParaEnviar);
            System.out.println("Segundo hilo recibió: " + datosRecibidos);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class ExchangerDemo {
    public static void main(String[] args) {
        Exchanger<Integer> exchanger = new Exchanger<>();

        Thread hilo1 = new Thread(new PrimerHilo(exchanger));
        Thread hilo2 = new Thread(new SegundoHilo(exchanger));

        hilo1.start();
        hilo2.start();
    }
}

Explicación del Código

  1. PrimerHilo y SegundoHilo:
    • Ambos implementan Runnable y utilizan un Exchanger<Integer>.
    • PrimerHilo envía el valor 10 y espera a recibir un valor de SegundoHilo.
    • SegundoHilo duerme durante 3 segundos antes de llamar a exchange(20), lo que simula un retraso en la sincronización.
    • Una vez que ambos hilos han alcanzado el punto de sincronización, intercambian los datos.

Salida Esperada

Primer hilo enviando: 10
(segundos de espera debido a Thread.sleep en SegundoHilo)
Segundo hilo enviando: 20
Primer hilo recibió: 20
Segundo hilo recibió: 10

Comparación entre Exchanger y Queue

Aunque Exchanger y Queue pueden usarse para la sincronización entre hilos, existen diferencias clave:

Característica Exchanger Queue (Cola)
Tipo de comunicación Bidireccional Unidireccional
Sincronización Ambos hilos deben esperar para intercambiar datos Puede haber productores y consumidores asíncronos
Uso recomendado Cuando dos hilos necesitan sincronizar e intercambiar datos de manera directa Cuando hay múltiples productores/consumidores y se necesita almacenamiento temporal de datos
Ejemplo de implementación Exchanger<T> BlockingQueue<T>

Además, Exchanger es similar a SynchronousQueue, que también bloquea hasta que ambos hilos estén listos para intercambiar datos. Sin embargo, SynchronousQueue es unidireccional, mientras que Exchanger permite el intercambio mutuo de objetos.

Conclusión

Exchanger es una herramienta útil en Java cuando dos hilos necesitan sincronizarse e intercambiar datos antes de continuar con sus tareas. Es especialmente útil en escenarios como:

  • Procesamiento en pasos encadenados donde cada paso es manejado por un hilo diferente.
  • Intercambio de buffers en sistemas de procesamiento de datos.
  • Simulaciones donde se necesita un mecanismo de sincronización preciso entre hilos.

Para escenarios con más de dos hilos o donde la sincronización no es obligatoria, las colas concurrentes (BlockingQueue) pueden ser una mejor opción.