⚙️ ¿Thread por conexión o por petición en Java? Comparativa explicada con tacos 🌮

Carlos ExpositoCarlos Exposito
3 min read

Cuando hablamos de servidores en Java, una gran pregunta aparece tarde o temprano:
¿Usamos un hilo por conexión o un hilo por cada petición?

En este artículo vamos a explicar de forma clara (y divertida) cómo funcionan estos dos modelos de concurrencia con sockets en Java. Veremos sus ventajas, desventajas, y te daré un ejemplo sabroso con... tacos. 🌮 ¡Vamos allá!


🌐 Conexión vs Petición: ¿Qué significa eso?

Primero, entendamos la diferencia:

  • Conexión: canal abierto entre cliente y servidor (como una llamada telefónica).
  • Petición: acción que se realiza dentro de una conexión (como hablar durante la llamada).

Una conexión puede tener varias peticiones si usamos HTTP Keep-Alive.


🧵 Modelo 1: Un hilo por conexión

Cada conexión con el cliente tiene su propio hilo, que se mantiene vivo mientras dure la conversación (es decir, mientras el socket esté abierto).

🍽️ Ejemplo: Servidor de pedidos de tacos

public class ServidorPorConexion {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(5050);
        System.out.println("🌮 Taquería abierta en el puerto 5050");

        while (true) {
            Socket cliente = serverSocket.accept();
            System.out.println("🌮 Cliente llegó: " + cliente.getInetAddress());
            new Thread(() -> manejarCliente(cliente)).start();
        }
    }

    private static void manejarCliente(Socket socket) {
        try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true)) {

            String pedido;
            while ((pedido = in.readLine()) != null) {
                System.out.println("🌮 Preparando: " + pedido);
                Thread.sleep(1000); // Simulamos tiempo de preparación
                out.println("✅ Pedido listo: " + pedido);
            }
        } catch (Exception e) {
            System.out.println("⚠️ Error con cliente: " + e.getMessage());
        }
    }
}

✅ Pros:

  • Muy fácil de entender e implementar.
  • Ideal para cuando hay pocas conexiones concurrentes.

❌ Contras:

  • Si hay muchas conexiones, necesitarás muchos hilos.
  • Los recursos se agotan rápido.

🔁 Modelo 2: Un hilo por cada petición

Cada petición, incluso dentro de una misma conexión, genera un nuevo hilo.

🌮 Ejemplo: Peticiones individuales por taco

public class ServidorPorPeticion {

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(6060);
        System.out.println("🌮 Servidor de tacos en el puerto 6060");

        List<Socket> clientes = new ArrayList<>();

        while (true) {
            aceptarClientes(serverSocket, clientes);
            procesarPeticiones(clientes);
        }
    }

    private static void aceptarClientes(ServerSocket serverSocket, List<Socket> clientes) {
        try {
            serverSocket.setSoTimeout(100);
            Socket cliente = serverSocket.accept();
            clientes.add(cliente);
            System.out.println("🙋‍♂️ Cliente nuevo conectado");
        } catch (IOException e) {
            // Timeout corto, ignoramos
        }
    }

    private static void procesarPeticiones(List<Socket> clientes) {
        for (Socket cliente : clientes) {
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(cliente.getInputStream()));
                if (in.ready()) {
                    String pedido = in.readLine();
                    new Thread(() -> {
                        try {
                            System.out.println("🌮 Preparando (por petición): " + pedido);
                            Thread.sleep(1000);
                            PrintWriter out = new PrintWriter(cliente.getOutputStream(), true);
                            out.println("✅ Taco servido: " + pedido);
                        } catch (Exception e) {
                            System.out.println("❌ Error en la petición: " + e.getMessage());
                        }
                    }).start();
                }
            } catch (IOException e) {
                System.out.println("⚠️ Cliente desconectado");
            }
        }
    }
}

✅ Pros:

  • Mejor escalabilidad con muchos usuarios haciendo muchas peticiones.
  • Se adapta mejor a cargas variables.

❌ Contras:

  • Muchos hilos pueden significar muchos context-switches (menos eficiencia).

🧮 Comparativa rápida

CaracterísticaHilo por ConexiónHilo por Petición
Ciclo de vidaLargo (hasta cerrar conexión)Corto (solo para una petición)
Cambios de contextoBajosAltos
EscalabilidadLimitada por nº de conexionesMejor, sin límite de conexiones
Ideal para...Apps con pocos clientes fijosApps con muchas peticiones variables

🧠 Conclusión

Ambos modelos tienen su lugar.

  • ¿Tienes pocos clientes pero muy activos? Hilo por conexión.
  • ¿Tu app recibe muchas peticiones concurrentes y no sabes cuántas vendrán? Hilo por petición.

Recuerda: en la práctica, usarás pools de hilos para mejorar aún más el rendimiento. Este artículo es solo el primer paso hacia servidores más avanzados.

¡Nos vemos en el próximo artículo! 😎

0
Subscribe to my newsletter

Read articles from Carlos Exposito directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Carlos Exposito
Carlos Exposito