Jest zasadnicza różnica pomiędzy sytuacją, gdy kilka wątków współbieżnie wykonuje ten sam kod lub modyfikuje stan tego samego obiektu a sytuacją, gdy kilka wątków, zupełnie niezależnie od siebie wykonuje rozłączny kod i modyfikuje stan różnych obiektów.
Klasa TestClassA z artykułu „Definiowanie wątków” tworzy jedną instancję klasy MyRunnable i uruchamia trzy wątki, które współbieżnie wykonują kod tej klasy i modyfikują jej stan. Jeśli więc jeden z wątków wykonuje instrukcję val = val + 1, to instrukcja ta powoduje zwiększenie o jeden wartości zmiennej val. Jeśli potem inny wątek wykonuje tą samą instrukcję na tym samym obiekcie, to zwiększa o jeden wartość dokładnie tej samej zmiennej. Po trzykrotnym wykonaniu instrukcji val = val + 1 zmienna val, która pierwotnie miała wartość 0, będzie miała wartość 3.
Dla odmiany, klasa TestClassB tworzy trzy instancje klasy MyRunnable, a więc będziemy mieli też trzy niezależne od siebie zmienne val. Jeśli więc jeden wątek wykonuje instrukcję val = val + 1 to zwiększa to wartość jednej zmiennej val, ale gdy chwilę potem inny wątek wykonuje tę samą instrukcję to zwiększa on wartość zupełnie innej zmiennej val. Mamy zatem po trzykrotnym wykonaniu instrukcji val = val + 1 trzy zmienne val, z których każda pierwotnie miała wartość 0, a finalnie ma wartość 1.
Co więcej, instrukcja val = val + 1 nie jest instrukcją atomową, tzn. nie jest to instrukcja która wykonywana jest zawsze w całości, od początku do końca. Z punktu widzenia programisty jest to jedna akcja, jednak z perspektywy Wirtualnej Maszyny Javy która tę instrukcję wykonuje jest to szereg akcji. Aby wykonać instrukcję val = val + 1 trzeba odczytać z odpowiedniej komórki w pamięci operacyjnej wartość zmiennej val, następnie trzeba obliczyć wynik operacji dodawania i na koniec zapisać ten wynik z powrotem do miejsca w pamięci, które odpowiada zmiennej val. Mamy więc trzy kroki i nie ma żadnej gwarancji co do tego, że wszystkie te trzy kroki wykonane będą bez przerwy, w sposób atomowy.
Prześledźmy teraz jeden z możliwych scenariuszy wykonania kodu klasy TestClassA. Uruchamiamy więc trzy wątki, z których każdy wykonuje ten sam kod, który to kod modyfikuje i następnie wyświetla wartość tej samej zmiennej val. Pamiętajmy, że instrukcja val = val + 1 składa się de facto z trzech instrukcji Wirtualnej Maszyny Javy.
Wirtualna Maszyna Javy rozpoczyna wykonanie pierwszego wątku i wykonuje instrukcję pobrania wartości zmiennej val z pamięci. Zmienna val ma inicjalnie wartość 0 i taka też wartość jest pobierana. W tym momencie algorytm zarządzający podziałem czasu procesora pomiędzy aktywne wątki zaimplementowany w Wirtualnej Maszynie Javy każe zaprzestać wykonania bieżącego wątku i podjąć się wykonania kolejnego. Aktualny wątek zostaje więc wstrzymany i rozpoczyna się wykonanie kolejnego. Przypomnijmy, że wartość zmiennej val została już pobrana z pamięci i jest to liczba 0.
Kolejny wątek odczytuje teraz wartość zmiennej val, która nadal wynosi 0, wykonuje operację dodawania (0 + 1) i zapisuje wartość 1 jako nową wartość zmiennej val. Wykonuje się jeszcze instrukcja wyświetlająca wartość zmiennej val na konsoli systemowej.
Wyświetlana jest wartość 1 i drugi wątek się kończy, a skoro bieżący wątek się zakończył, to Wirtualna Maszyna Javy rozpoczyna wykonanie któregoś z pozostałych dwu wątków. Przypuśćmy, że ponownie pada na wątek pierwszy. Wątek ten zdążył już uprzednio pobrać wartość zmiennej val – wartość 0 – i teraz wykonuje operację dodawania (0 + 1) oraz przypisuje wynik do zmiennej val. Do zmiennej val która miała wartość 1, przypisaną przez wątek drugi, ponownie przypisywana jest wartość 1. Wątek ten wyświetla jeszcze bieżącą wartość zmiennej val, tj. liczbę 1 i kończy się.
Pozostaje już tylko wykonać wątek trzeci, który nie ma konkurencji w postaci innych aktywnych wątków, więc zostanie wykonany w sposób niezakłócony, od góry do dołu, i przypisze zmiennej val wartość 2, oraz wyświetli tę wartość na ekranie.
Mamy więc w rezultacie do zmiennej val przypisaną wartość 2 oraz ciąg liczb 1, 1, 2 wyświetlony na ekranie. Oczywiście jest to tylko jeden z bardzo wielu możliwych scenariuszy.
Skoro program w którym kilka wątków współbieżnie wykonuje ten sam kod, lub modyfikuje w inny sposób stan tej samej zmiennej, może być tak niebezpieczny, to czy oznacza to, że takiego kodu nie należy pisać? Oczywiście nie. Taki kod po prostu trzeba odpowiednio synchronizować, o czym w kolejnym odcinku kursu, odcinku „Synchronizacja wątków”.