Sobes.tech
Middle

¿Por qué ocurren las carreras de hilos y qué métodos existen para combatirlas?

sobes.tech AI

Respuesta de la IA

Las condiciones de carrera (race conditions) ocurren cuando varios hilos acceden simultáneamente a datos compartidos modificables, y el resultado depende del orden en que se ejecutan. Esto sucede porque el sistema operativo puede cambiar de hilo en cualquier momento, interrumpiendo su ejecución.

Formas de combatirlo:

  • Sincronización: Uso de primitivas de sincronización para garantizar acceso exclusivo a recursos compartidos.

    • Palabra clave synchronized: Se aplica a métodos o bloques de código. Bloquea el acceso a un objeto o clase para otros hilos hasta que el hilo actual termine de ejecutar el bloque/método sincronizado.

      // Sincronización de método
      public synchronized void updateData(int newValue) {
          // ... actualización de datos compartidos
      }
      
      // Sincronización de bloque de código
      public void processData() {
          synchronized (this) { // O cualquier otro objeto monitor
              // ... acceso a datos compartidos
          }
      }
      
    • Bloqueos (Lock): Ofrecen un control más flexible sobre la sincronización en comparación con synchronized.

      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      private final Lock dataLock = new ReentrantLock();
      
      public void safeUpdateData(int newValue) {
          dataLock.lock(); // Adquisición del bloqueo
          try {
              // ... actualización de datos compartidos
          } finally {
              dataLock.unlock(); // Liberación del bloqueo
          }
      }
      
  • Variables atómicas (Atomic...): Proporcionan operaciones seguras para hilos sobre tipos primitivos sin bloqueo explícito. Se usan para operaciones simples (incremento, comparación y cambio).

    import java.util.concurrent.atomic.AtomicInteger;
    
    private final AtomicInteger counter = new AtomicInteger(0);
    
    public void incrementCounter() {
        counter.incrementAndGet(); // Incremento atómico
    }
    
  • Colecciones seguras para hilos: Uso de colecciones especiales del paquete java.util.concurrent (por ejemplo, ConcurrentHashMap, CopyOnWriteArrayList), diseñadas para acceso concurrente.

    import java.util.concurrent.ConcurrentHashMap;
    import java.util.Map;
    
    private final Map<String, Integer> safeMap = new ConcurrentHashMap<>();
    
  • Objetos inmutables (Immutable): Si los datos no cambian después de su creación, se puede acceder a ellos de forma segura desde múltiples hilos sin sincronización.

  • Variables locales de hilo (ThreadLocal): A cada hilo se le asigna una copia propia de la variable, lo que evita la competencia por acceso.

    private final ThreadLocal<Integer> threadId = new ThreadLocal<>();
    
    public void setThreadId() {
        threadId.set(Thread.currentThread().getId());
    }
    
    public int getThreadId() {
        return threadId.get();
    }
    
  • Diseño arquitectónico correcto: Minimizar datos modificables compartidos y usar enfoques asincrónicos o transmisión de mensajes entre hilos puede reducir la probabilidad de condiciones de carrera.