El objetivo de este taller es aprender los conceptos fundamentales de multi-threading en Java, implementando ejemplos prácticos para entender cómo manejar múltiples hilos en una aplicación Java.
- Conocimientos básicos de Java.
- Familiaridad con conceptos de concurrencia y threading.
Para este taller, podemos utilizar una configuración básica de Maven:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>multi-threading</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- Dependencias estándar -->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>Código:
public class ThreadExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Thread: " + i);
try {
Thread.sleep(1000); // Pausa el hilo por 1 segundo
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start(); // Inicia el hilo
}
}Explicación:
- Se crea un nuevo hilo utilizando la clase
Thread. - La lógica del hilo se define dentro de un bloque
Runnableproporcionado al constructor deThread. - El hilo imprime un contador de 0 a 4, pausando por 1 segundo entre cada impresión.
thread.start()inicia la ejecución del hilo.
Código:
public class RunnableExample {
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Runnable: " + i);
try {
Thread.sleep(1000); // Pausa el hilo por 1 segundo
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(task); // Crea un hilo con el Runnable
thread.start(); // Inicia el hilo
}
}Explicación:
- Se define una tarea
Runnableque imprime un contador de 0 a 4, pausando por 1 segundo entre cada impresión. - Se crea un nuevo hilo utilizando el
Runnablecomo argumento para el constructor deThread. thread.start()inicia la ejecución del hilo.
Código:
public class SynchronizedExample {
private static int counter = 0;
public static synchronized void increment() {
counter++;
}
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
increment(); // Incrementa el contador de manera sincronizada
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
try {
thread1.join(); // Espera a que thread1 termine
thread2.join(); // Espera a que thread2 termine
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Counter: " + counter); // Imprime el valor final del contador
}
}Explicación:
- Se define un método
increment()que incrementa un contador de manera sincronizada. - Dos hilos ejecutan una tarea que llama al método
increment()1000 veces. thread1.join()ythread2.join()aseguran que el programa principal espere a que ambos hilos terminen antes de continuar.- El valor final del contador se imprime al final.
Código:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorServiceExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2); // Crea un pool de 2 hilos
Runnable task1 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Task 1: " + i);
try {
Thread.sleep(1000); // Pausa el hilo por 1 segundo
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable task2 = () -> {
for (int i = 0; i < 5; i++) {
System.out.println("Task 2: " + i);
try {
Thread.sleep(1000); // Pausa el hilo por 1 segundo
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
executor.submit(task1); // Envía la tarea 1 al pool de hilos
executor.submit(task2); // Envía la tarea 2 al pool de hilos
executor.shutdown(); // Finaliza el pool de hilos una vez completadas las tareas
}
}Explicación:
- Se crea un
ExecutorServicecon un pool de 2 hilos. - Se definen dos tareas
Runnableque imprimen un contador de 0 a 4, pausando por 1 segundo entre cada impresión. - Las tareas se envían al pool de hilos utilizando
executor.submit(). executor.shutdown()finaliza el pool de hilos una vez que todas las tareas han sido completadas.
Código:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2); // Crea un pool de 2 hilos
Callable<Integer> task = () -> {
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += i;
try {
Thread.sleep(1000); // Pausa el hilo por 1 segundo
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return sum; // Retorna la suma de los números
};
Future<Integer> future = executor.submit(task); // Envía la tarea y recibe un Future
try {
Integer result = future.get(); // Obtiene el resultado de la tarea
System.out.println("Sum: " + result); // Imprime el resultado
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
executor.shutdown(); // Finaliza el pool de hilos
}
}Explicación:
- Se crea un
ExecutorServicecon un pool de 2 hilos. - Se define una tarea
Callableque calcula la suma de los números del 0 al 4, pausando por 1 segundo entre cada iteración. - La tarea se envía al pool de hilos utilizando
executor.submit(), y se recibe unFutureque representará el resultado de la tarea. future.get()se utiliza para obtener el resultado de la tarea una vez que se ha completado.executor.shutdown()finaliza el pool de hilos una vez que todas las tareas han sido completadas.
Instrucciones para los Estudiantes:
- Definir un Caso de Uso:
- Piensa en un escenario donde el multi-threading pueda ser útil. Por ejemplo, procesamiento paralelo de datos, tareas de I/O concurrentes, etc.
- Seleccionar la Estrategia de Multi-threading:
- Elige la estrategia de multi-threading adecuada para tu caso de uso (
Thread,Runnable,ExecutorService, etc.).
- Elige la estrategia de multi-threading adecuada para tu caso de uso (
- Implementar el Ejemplo:
- Implementa tu ejemplo en Java utilizando la estrategia seleccionada.
- Asegúrate de comentar tu código para explicar qué hace cada parte y cómo se gestionan los hilos.
- Probar y Depurar:
- Ejecuta tu código y verifica que funcione como esperas. Asegúrate de manejar cualquier posible error.
- Compartir y Discutir:
- Comparte tu código con los demás estudiantes del taller. Discutan sobre las diferentes implementaciones y enfoques utilizados.
Ejemplo de Código para los Estudiantes:
Aquí tienes un ejemplo sencillo para inspirarte. Supongamos que queremos procesar una lista de tareas en paralelo y sumar los resultados.
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CustomThreadExample {
public static void main(String[] args) {
List<Callable<Integer>> tasks = Arrays.asList(
() -> { Thread.sleep(1000); return 10; },
() -> { Thread.sleep(1000); return 20; },
() -> { Thread.sleep(1000); return 30; }
);
ExecutorService executor = Executors.newFixedThreadPool(3);
try {
List<Future<Integer>> results = executor.invokeAll(tasks);
int sum = 0;
for (Future<Integer> result : results) {
sum += result.get();
}
System.out.println("Sum: " + sum);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executor.shutdown();
}
}
}- Aprendimos a utilizar diversas estrategias de multi-threading en Java:
Thread,Runnable,synchronized,ExecutorService,Callable, yFuture. - Implementamos ejemplos prácticos para entender cómo manejar y sincronizar múltiples hilos en una aplicación Java.
- Los estudiantes crearon sus propios ejemplos para aplicar lo aprendido.
El multi-threading es una poderosa herramienta para mejorar el rendimiento y la capacidad de respuesta de las aplicaciones. Las estrategias y técnicas que hemos explorado en este taller son fundamentales para construir aplicaciones concurrentes y escalables. Continuar practicando con estos conceptos y explorando otros aspectos de la concurrencia te permitirá construir aplicaciones más robustas y eficientes.
- Configura tu entorno de desarrollo con las dependencias necesarias.
- Implementa y ejecuta cada ejemplo de multi-threading en Java.
- Crea y comparte tu propio ejemplo utilizando las estrategias aprendidas.
- Experimenta con otras técnicas de concurrencia y casos de uso para profundizar en tu comprensión del multi-threading.
Este taller proporciona una base sólida para comenzar a trabajar con multi-threading en Java y fomenta la creación de ejemplos propios para reforzar el aprendizaje.