Los hilos en Java te permiten ejecutar múltiples tareas de forma concurrente, lo que es útil para aprovechar al máximo los recursos del sistema y mejorar el rendimiento de las aplicaciones.
Puedes crear hilos en Java extendiendo la clase Thread
o implementando la interfaz Runnable
.
public class MiHilo extends Thread {
public void run() {
// Código a ejecutar en el hilo
for (int i = 0; i < 5; i++) {
System.out.println("Hola desde el hilo " + i);
try {
Thread.sleep(1000); // Esperar 1 segundo
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MiHilo hilo = new MiHilo();
hilo.start(); // Iniciar el hilo
}
}
public class MiRunnable implements Runnable {
public void run() {
// Código a ejecutar en el hilo
for (int i = 0; i < 5; i++) {
System.out.println("Hola desde el hilo " + i);
try {
Thread.sleep(1000); // Esperar 1 segundo
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MiRunnable miRunnable = new MiRunnable();
Thread hilo = new Thread(miRunnable);
hilo.start(); // Iniciar el hilo
}
}
En este ejemplo:
MiRunnable
que implementa la interfaz Runnable
y define el método run()
que contiene el código que se ejecutará en el hilo.Thread
pasando el objeto MiRunnable
en su constructor.start()
.Thread
.La sincronización en Java es importante cuando múltiples hilos acceden y modifican recursos compartidos para evitar condiciones de carrera y garantizar la consistencia de los datos.
Puedes utilizar la palabra clave synchronized
para sincronizar bloques de código o métodos.
public class Contador {
private int count = 0;
public void increment() {
synchronized(this) {
count++;
}
}
public int getCount() {
return count;
}
}
En este ejemplo, el bloque de código dentro del método increment()
está sincronizado utilizando synchronized(this)
, lo que garantiza que solo un hilo pueda ejecutar ese bloque a la vez.
public class Contador {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
En este caso, los métodos increment()
y getCount()
están sincronizados, lo que significa que solo un hilo puede ejecutar cualquiera de estos métodos a la vez.
También puedes sincronizar bloques de código estáticos utilizando un objeto de clase.
public class Contador {
private static int count = 0;
public static void increment() {
synchronized(Contador.class) {
count++;
}
}
public static int getCount() {
return count;
}
}
En este ejemplo, el bloque de código estático está sincronizado utilizando synchronized(Contador.class)
.
Al trabajar con hilos en Java, es importante tener en cuenta algunos problemas comunes que pueden surgir debido a la concurrencia, como el deadlock y las race conditions.
El deadlock ocurre cuando dos o más hilos se bloquean mutuamente esperando recursos que se están utilizando por otros hilos, lo que hace que todos los hilos queden bloqueados y la aplicación se quede inactiva.
public class DeadlockEjemplo {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
Thread hilo1 = new Thread(() -> {
synchronized(lock1) {
System.out.println("Hilo 1: Obtuvo el lock1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock2) {
System.out.println("Hilo 1: Obtuvo el lock2");
}
}
});
Thread hilo2 = new Thread(() -> {
synchronized(lock2) {
System.out.println("Hilo 2: Obtuvo el lock2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock1) {
System.out.println("Hilo 2: Obtuvo el lock1");
}
}
});
hilo1.start();
hilo2.start();
}
}
En este ejemplo, hilo1
bloquea lock1
y espera lock2
, mientras que hilo2
bloquea lock2
y espera lock1
. Ambos hilos quedan bloqueados esperando los recursos que están siendo utilizados por el otro hilo, lo que resulta en un deadlock.
Las race conditions ocurren cuando múltiples hilos intentan modificar el mismo recurso compartido al mismo tiempo sin una sincronización adecuada, lo que puede llevar a resultados inesperados o inconsistentes.
public class RaceConditionEjemplo {
private static int count = 0;
public static void main(String[] args) {
Thread hilo1 = new Thread(() -> {
for (int i = 0; i < 1000000; i++) {
count++;
}
});
Thread hilo2 = new Thread(() -> {
for (int i = 0; i < 1000000; i++) {
count++;
}
});
hilo1.start();
hilo2.start();
// Esperar a que los hilos terminen
try {
hilo1.join();
hilo2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Valor de count: " + count);
}
}
En este ejemplo, ambos hilos incrementan la variable count
en un millón de veces, pero debido a la falta de sincronización, pueden interferirse mutuamente y el resultado final podría no ser dos millones como se espera.