Kategorie szkoleń | Egzaminy | Kontakt

Odpowiedź (1)

  • 0

Załóżmy, że mamy następujący problem:
W programie działają dwa wątki, z których jeden w pętli zadaną ilość razy inkrementuje współdzieloną zmienną (np. typu int), a drugi w pętli tę samą ilość razy tę zmienną dekrementuje. Tak więc klasa z metodami wywoływanymi przez wątki może wyglądać następująco:

public class CounterTest {
  private volatile int counter;
  
  public void incr() { // metoda wołana w pętli przez wątek #1
    counter++;
  }

  public void decr() { // metoda wołana w pętli przez wątek #2
    counter--;
  }

  public int getCounter() {
    return counter;
  }
}

 

Pole counter jest zadeklarowane jako volatile (wartość ulotna), aby wątki widziały zmiany dokonywane przez siebie (wyłączana jest optymalizacja umożliwiająca cache'owanie wartości zmiennej np. w rejestrze procesora).
Mamy problem: czy w przedstawionej sytuacji jest konieczne synchronizowanie metod incr() i decr()?

Na pierwszy rzut oka, wydawałoby się, że nie, gdyż są to pojedyncze instrukcje. To jednak nie jest dobrym rozwiązaniem, gdyż mimo że instrukcje są pojedyncze, to nie są operacjami atomowymi (niepodzielnymi). Jeśli obejrzymy, jak wygląda kod bajtowy takich instrukcji (można to zrobić za pomocą narzędzia javap, dostępnego w JDK), to zobaczymy, że operacja składa się z szeregu kroków, takich jak:

  • odczyt wartości z pamięci do rejestru procesora
  • inkrementacja/dekrementacja wartości
  • zapis wyniku do pamięci

Skoro tak, to oznacza, że w dowolnym momencie ten cykl może być przerwany z powodu wywłaszczenia wątka, co może doprowadzić do niespójności danych.

Innym rozwiązaniem problemu, poza jawną synchronizacją metod, jest np. użycie typów atomowych. Rozwiązanie to jest dostępne od Javy SE 5.0.

  • Odpowiedział
  • @ | 27.06.2013
  • TRENER MODERATOR ALTKOM AKADEMII