Introducción a los Threads en Java
En Java, un Thread (hilo) es la unidad más pequeña de ejecución dentro de un proceso. Los hilos permiten la ejecución concurrente de tareas, lo que significa que múltiples tareas pueden ejecutarse al mismo tiempo dentro de un mismo programa. Esto es especialmente útil en aplicaciones que requieren multitarea, como servidores, interfaces gráficas, o juegos.
Características
- Concurrencia: Los hilos permiten que varias tareas se ejecuten simultáneamente.
- Independencia: Cada hilo tiene su propio flujo de ejecución, pila y estado.
- Comunicación: Los hilos pueden comunicarse entre sí mediante métodos como wait(), notify(), y notifyAll().
- Sincronización: Los hilos pueden sincronizarse para evitar condiciones de carrera y garantizar la consistencia de los datos.
Métodos principales de la clase Thread
start(): Inicia la ejecución del hilo.run(): Contiene el código que el hilo ejecutará.sleep(long millis): Pausa la ejecución del hilo durante un tiempo especificado.join(): Espera a que el hilo termine su ejecución.interrupt(): Interrumpe la ejecución del hilo.isAlive(): Verifica si el hilo está en ejecución.
Figura 1: Representación de hilos de ejecución.
Práctica: Juego Básico con Threads
Objetivo
Conocer el funcionamiento de la programación concurrente (hilos, Threads) mediante la implementación de un juego básico donde un jugador debe esquivar obstáculos que caen desde la parte superior de la pantalla.
El jugador se moverá horizontalmente usando las teclas de flecha, y los obstáculos caerán a diferentes velocidades. Utilizaremos hilos para manejar el movimiento del jugador y la caída de los obstáculos. Todas las clases se desarrollan en el mismo paquete.
Figura 2: Estructura visual del juego a desarrollar.
Paso 1: Crear la clase Jugador
El jugador será representado por un objeto que se moverá horizontalmente.
public class Jugador extends Thread {
private int posicionX;
private boolean corriendo;
public Jugador() {
this.posicionX = 0;
this.corriendo = true;
}
public void moverDerecha() {
if (posicionX < 9) posicionX++;
}
public void moverIzquierda() {
if (posicionX > 0) posicionX--;
}
public int getPosicionX() {
return posicionX;
}
@Override
public void run() {
while (corriendo) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void detener() {
corriendo = false;
}
}
Paso 2: Crear la clase Obstaculo
Los obstáculos caerán desde la parte superior de la pantalla.
class Obstaculo extends Thread {
private int posicionX;
private int posicionY;
private int velocidad;
private boolean activo;
public Obstaculo(int velocidad, int posicionX) {
this.posicionX = posicionX;
this.posicionY = 0;
this.velocidad = velocidad;
this.activo = true;
}
public int getPosicionX() { return posicionX; }
public int getPosicionY() { return posicionY; }
@Override
public void run() {
while (activo && posicionY < 10) {
posicionY++;
try {
Thread.sleep(velocidad);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
activo = false;
}
public boolean estaActivo() { return activo; }
}
Paso 3: Crear la clase Juego
Esta clase manejará la lógica del juego y la interacción entre el jugador y los obstáculos.
import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Juego {
private Jugador jugador;
private List<Obstaculo> obstaculos;
private JFrame ventana;
private PanelJuego panelJuego;
private boolean juegoActivo;
public Juego() {
jugador = new Jugador();
obstaculos = new ArrayList<>();
juegoActivo = true;
ventana = new JFrame("Juego con Threads");
ventana.setSize(500, 500);
ventana.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panelJuego = new PanelJuego(jugador, obstaculos);
ventana.add(panelJuego);
ventana.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
jugador.moverIzquierda();
} else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
jugador.moverDerecha();
}
}
});
ventana.setFocusable(true);
ventana.setVisible(true);
}
public void iniciar() {
jugador.start();
Thread generadorObstaculos = new Thread(() -> {
Random random = new Random();
while (juegoActivo) {
Obstaculo obstaculo = new Obstaculo(random.nextInt(500) + 100, random.nextInt(10));
obstaculos.add(obstaculo);
obstaculo.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
generadorObstaculos.start();
while (juegoActivo) {
panelJuego.repaint();
for (Obstaculo obstaculo : obstaculos) {
if (obstaculo.getPosicionY() == 9 && jugador.getPosicionX() == obstaculo.getPosicionX()) {
juegoActivo = false;
JOptionPane.showMessageDialog(ventana, "¡Colisión! Fin del juego.");
jugador.detener();
for (Obstaculo o : obstaculos) { o.interrupt(); }
return;
}
}
obstaculos.removeIf(obstaculo -> !obstaculo.estaActivo());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Paso 4: Crear la clase PanelJuego
Esta clase servirá para visualizar el juego en la pantalla.
import javax.swing.*;
import java.awt.*;
import java.util.List;
public class PanelJuego extends javax.swing.JPanel {
private Jugador jugador;
private List<Obstaculo> obstaculos;
public PanelJuego(Jugador jugador, List<Obstaculo> obstaculos) {
this.jugador = jugador;
this.obstaculos = obstaculos;
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(jugador.getPosicionX() * 50, 450, 50, 50);
g.setColor(Color.RED);
for (Obstaculo obstaculo : obstaculos) {
if (obstaculo.estaActivo()) {
g.fillRect(obstaculo.getPosicionX() * 50, obstaculo.getPosicionY() * 50, 50, 50);
}
}
}
}
Paso 5: Desarrollar la clase main() y ejecutar
La clase main es la clase principal. En esta se realizan las instancias para que el juego se pueda ejecutar.
public class Threads {
public static void main(String[] args) {
Juego MiJuego = new Juego();
MiJuego.iniciar();
}
}
Actividad
Identifica en cada una de las clases codificadas el uso de los hilos. Haz una lista de líneas de código que utilizaron hilos y explica qué función tiene cada línea. Escribe tus respuestas en el panel de notas de la derecha.
Conclusión y Recomendaciones
Resumen
Threads permiten la ejecución concurrente de tareas en Java. En esta práctica, utilizamos hilos para manejar el movimiento del jugador y la caída de los obstáculos en un juego básico. Los métodos start(), run(), sleep(), y interrupt() son fundamentales para controlar la ejecución de los hilos.
Recomendaciones
- Sincronización: Asegúrate de sincronizar el acceso a recursos compartidos para evitar condiciones de carrera.
- Manejo de excepciones: Siempre maneja las excepciones en los hilos para evitar que el programa se detenga inesperadamente.
- Evitar bloqueos: Diseña cuidadosamente la lógica de los hilos para evitar bloqueos (deadlocks).
- Utiliza
join()cuando necesites esperar a que un hilo termine antes de continuar con la ejecución.
¡Bienvenidos al Sprint Challenge! ⏱️
Tiempo máximo: 40 min
El día de hoy pondremos a prueba su capacidad de respuesta en el dominio de MultiThreads y la creatividad visual en una carrera contra el reloj.
El Desafío
Partiendo de su proyecto base de juego con hilos, deberán realizar las siguientes modificaciones en un máximo de 40 minutos:
- Modo Multijugador: Implementar un hilo adicional de ejecución para controlar a un segundo jugador de forma independiente.
- Modo Multiobstáculos: Implementar un hilo adicional de ejecución para controlar a un segundo obstáculo de forma independiente.
- Rediseño Visual: Modificar creativamente los assets (imágenes/sprites) de los obstáculos y de ambos jugadores. No se permite el diseño original. Puedes usar alguna herramienta de IA.
- Lógica Funcional: Ambos jugadores deben poder interactuar con el entorno (colisiones y movimiento) sin interferir negativamente con el hilo del otro.
Criterios de Evaluación
| Criterio | Puntuación Esperada (1-5) |
|---|---|
| Sincronización: Funcionamiento fluido de los hilos sin bloqueos. | [ 1 - 2 - 3 - 4 - 5 ] |
| Lógica de Colisión: Detección correcta de impactos para ambos jugadores. | [ 1 - 2 - 3 - 4 - 5 ] |
| Identidad Visual: Creatividad y originalidad en el cambio de imágenes. | [ 1 - 2 - 3 - 4 - 5 ] |
| Jugabilidad: Controles claros y experiencia de juego estable. | [ 1 - 2 - 3 - 4 - 5 ] |
Instrucciones de Entrega 📱
Entregar el juego funcionando, con los requerimientos solicitados.
ocurre