Sobes.tech
Middle

Kodėl kyla sriegių lenktynės ir kokie būdai jų kovai yra?

sobes.tech AI

Atsakymas iš AI

Lenktynės sąlygos (race conditions) atsiranda, kai keli srautai vienu metu prieina prie bendrų keičiamų duomenų, ir jų vykdymo rezultatas priklauso nuo jų vykdymo tvarkos. Tai vyksta todėl, kad operacinė sistema gali bet kuriuo metu perjungti srautus, nutraukdama jų vykdymą.

Kovos būdai:

  • Sinchronizacija: Naudojant sinchronizacijos primitivius, siekiant užtikrinti išskirtinį prieigą prie bendrų išteklių.

    • synchronized raktinis žodis: Taikomas metodams arba kodo blokams. Blokuoja prieigą prie objekto arba klasės kitiems srautams, kol dabartinis srautas nebaigs sinchronizuoto bloko/metodo vykdymo.

      // Sinchronizuotas metodas
      public synchronized void updateData(int newValue) {
          // ... bendrų duomenų atnaujinimas
      }
      
      // Sinchronizuotas kodo blokas
      public void processData() {
          synchronized (this) { // Arba kitas monitoriaus objektas
              // ... prieiga prie bendrų duomenų
          }
      }
      
    • Užraktai (Lock): Siūlo lankstesnę kontrolę nei 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(); // Užrakinimo įsigijimas
          try {
              // ... bendrų duomenų atnaujinimas
          } finally {
              dataLock.unlock(); // Užrakinimo atlaisvinimas
          }
      }
      
  • Atomarinės kintamosios (Atomic...): Užtikrina srautų saugias operacijas su primityviais tipais be aiškaus užrakinimo. Naudojamos paprastoms operacijoms (padidinimui, palyginimui ir keitimui).

    import java.util.concurrent.atomic.AtomicInteger;
    
    private final AtomicInteger counter = new AtomicInteger(0);
    
    public void incrementCounter() {
        counter.incrementAndGet(); // Atominis padidinimas
    }
    
  • Srautų saugios kolekcijos: Naudojant specialias kolekcijas iš java.util.concurrent paketo (pvz., ConcurrentHashMap, CopyOnWriteArrayList), kurios skirtos daugiasračių prieigai.

    import java.util.concurrent.ConcurrentHashMap;
    import java.util.Map;
    
    private final Map<String, Integer> safeMap = new ConcurrentHashMap<>();
    
  • Nekeitė (Immutable) objektai: Jei duomenys nesikeičia po sukūrimo, jais galima saugiai naudotis iš kelių srautų be sinchronizacijos.

  • ThreadLocal srautų vietiniai kintamieji: Kiekvienam srautui priskiriama savo kopija, kas išvengia konkurencijos dėl prieigos.

    private final ThreadLocal<Integer> threadId = new ThreadLocal<>();
    
    public void setThreadId() {
        threadId.set(Thread.currentThread().getId());
    }
    
    public int getThreadId() {
        return threadId.get();
    }
    
  • Teisingas architektūros projektavimas: Bendrų keičiamų duomenų minimizavimas ir asinchroninių metodų ar žinučių perdavimo tarp srautų naudojimas gali sumažinti lenktynių sąlygų tikimybę.