Sobes.tech
Middle

De ce apar condițiile de cursă a firelor și ce metode există pentru a le combate?

sobes.tech AI

Răspuns de la AI

Condițiile de cursă (race conditions) apar atunci când mai multe fire de execuție accesează simultan date modificate partajate, iar rezultatul depinde de ordinea în care sunt executate. Acest lucru se întâmplă deoarece sistemul de operare poate comuta între fire în orice moment, întrerupând execuția lor.

Modalități de combatere:

  • Sincronizare: Utilizarea primitive de sincronizare pentru a asigura accesul exclusiv la resursele partajate.

    • Cuvântul cheie synchronized: Se aplică metodelor sau blocurilor de cod. Blochează accesul la un obiect sau clasă pentru alte fire până când firul curent finalizează execuția blocului/metodei sincronizate.

      // Sincronizarea metodei
      public synchronized void updateData(int newValue) {
          // ... actualizarea datelor comune
      }
      
      // Sincronizarea unui bloc de cod
      public void processData() {
          synchronized (this) { // Sau orice alt obiect monitor
              // ... accesul la datele comune
          }
      }
      
    • Blochează (Lock): Oferă un control mai flexibil asupra sincronizării comparativ cu 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(); // Obținerea blocajului
          try {
              // ... actualizarea datelor comune
          } finally {
              dataLock.unlock(); // Eliberarea blocajului
          }
      }
      
  • Variabile atomice (Atomic...): Oferă operații sigure pentru fire pe tipuri primitive fără blocare explicită. Se utilizează pentru operații simple (incrementare, comparație și schimb).

    import java.util.concurrent.atomic.AtomicInteger;
    
    private final AtomicInteger counter = new AtomicInteger(0);
    
    public void incrementCounter() {
        counter.incrementAndGet(); // Creștere atomică
    }
    
  • Colecții sigure pentru fire: Utilizarea colecțiilor speciale din pachetul java.util.concurrent (de exemplu, ConcurrentHashMap, CopyOnWriteArrayList), proiectate pentru acces concurent.

    import java.util.concurrent.ConcurrentHashMap;
    import java.util.Map;
    
    private final Map<String, Integer> safeMap = new ConcurrentHashMap<>();
    
  • Obiecte imutabile (Immutable): Dacă datele nu se schimbă după crearea lor, pot fi accesate în siguranță din mai multe fire fără sincronizare.

  • Variabile ThreadLocal: Fiecare fir primește o copie proprie a variabilei, evitând competiția pentru acces.

    private final ThreadLocal<Integer> threadId = new ThreadLocal<>();
    
    public void setThreadId() {
        threadId.set(Thread.currentThread().getId());
    }
    
    public int getThreadId() {
        return threadId.get();
    }
    
  • Proiectare arhitecturală corectă: Minimizarea datelor modificate partajat și utilizarea abordărilor asincrone sau a transmiterii mesajelor între fire poate reduce probabilitatea apariției condițiilor de cursă.