Sobes.tech
Middle

Защо възникват състезания между нишки и какви методи съществуват за борба с тях?

sobes.tech AI

Отговор от AI

Състезателни условия (race conditions) възникват, когато няколко нишки едновременно достъпват общи променящи се данни и резултатът от изпълнението зависи от реда им на изпълнение. Това се случва, защото операционната система може да превключва нишки по всяко време, прекъсвайки изпълнението им.

Начини за борба:

  • Синхронизация: Използване на примитиви за синхронизация за осигуряване на изключителен достъп до общите ресурси.

    • Ключовата дума synchronized: Прилага се към методи или блокове от код. Блокира достъпа до обект или клас за други нишки, докато текущата нишка не завърши изпълнението на синхронизирания блок/метод.

      // Синхронизирана метода
      public synchronized void updateData(int newValue) {
          // ... актуализиране на общите данни
      }
      
      // Синхронизиране на блок от код
      public void processData() {
          synchronized (this) { // Или друг монитор обект
              // ... достъп до общите данни
          }
      }
      
    • Блокировки (Lock): Предоставят по-гъвкав контрол върху синхронизацията в сравнение с 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(); // Взимане на блокиране
          try {
              // ... актуализиране на общите данни
          } finally {
              dataLock.unlock(); // Освобождаване на блокиране
          }
      }
      
  • Атомарни променливи (Atomic...): Предоставят потокобезопасни операции върху примитивни типове без явна блокировка. Използват се за прости операции (инкрементиране, сравнение и обмен).

    import java.util.concurrent.atomic.AtomicInteger;
    
    private final AtomicInteger counter = new AtomicInteger(0);
    
    public void incrementCounter() {
        counter.incrementAndGet(); // Атомарно увеличение
    }
    
  • Потокобезопасни колекции: Използване на специални колекции от пакета java.util.concurrent (например, ConcurrentHashMap, CopyOnWriteArrayList), които са проектирани за многопоточен достъп.

    import java.util.concurrent.ConcurrentHashMap;
    import java.util.Map;
    
    private final Map<String, Integer> safeMap = new ConcurrentHashMap<>();
    
  • Неизменяеми (Immutable) обекти: Ако данните не се променят след създаването им, към тях може безопасно да се обръща от няколко потока без синхронизация.

  • Локални променливи на нишки (ThreadLocal): На всяка нишка се присвоява собствена копия на променливата, което изключва конкуренцията за достъп.

    private final ThreadLocal<Integer> threadId = new ThreadLocal<>();
    
    public void setThreadId() {
        threadId.set(Thread.currentThread().getId());
    }
    
    public int getThreadId() {
        return threadId.get();
    }
    
  • Правилен дизайн на архитектурата: Минимизиране на споделените променящи се данни и използване на асинхронни подходи или предаване на съобщения между нишките може да намали вероятността от възникване на гонки.