– Cześć, Amigo! Wczoraj rozmawialiśmy o korzyściach i udogodnieniach związanych z wielowątkowością. Teraz pora przyjrzeć się jej wadom. A, niestety, nie są one bez znaczenia.

Wcześniej patrzyliśmy na program jako na zbiór obiektów, które wywołują wzajemnie swoje metody. Teraz wszystko się trochę skomplikuje. Programowi bliżej do zbioru obiektów, który ma w sobie kilka „małych robocików” (wątków) poruszających się po nim i wykonujących polecenia zawarte w metodach.

Ta nowa interpretacja nie wyklucza pierwszej. Wciąż są one obiektami i nadal wywołują nawzajem swoje metody. Musimy jednak pamiętać, że wątków jest kilka, a każdy z nich wykonuje swoją własną pracę lub zadanie.

Program staje się coraz bardziej skomplikowany. Różne wątki zmieniają status różnych obiektów na podstawie zadań, które wykonują. I mogą wchodzić sobie nawzajem w drogę.

Ale najgorsze jest to, co dzieje się głęboko wewnątrz maszyny Java... Jak już mówiłem, pozorna równoczesność wątków jest osiągana dzięki temu, że procesor ciągle przełącza się z jednego wątku na drugi. Przełącza się na wątek, pracuje przez 10 milisekund, przechodzi do następnego wątku, pracuje przez 10 milisekund i tak dalej. I właśnie tu jest pies pogrzebany: owe przejścia mogą występować w najbardziej nieodpowiednich momentach. Przeanalizuj ten przykład:

Kod pierwszego wątku Kod drugiego wątku
System.out.print("Nick ma");
System.out.print("");
System.out.print("15");
System.out.print("");
System.out.print("lat");
System.out.println();
System.out.print("Lena ma");
System.out.print("");
System.out.print("21");
System.out.print("");
System.out.print("lat");
System.out.println();
Oczekiwaliśmy, że zostanie wyświetlone
Nick ma 15 lat
Lena ma 21 lat
Rzeczywiste wykonanie kodu Kod pierwszego wątku Kod drugiego wątku
System.out.print("Nick ma");
System.out.print("Lena ma");
System.out.print(" ");
System.out.print(" ");
System.out.print("15");
System.out.print("21");
System.out.print(" ");
System.out.print(" ");
System.out.print("lat");
System.out.println();
System.out.print("lat");
System.out.println();
System.out.print("Nick ma");
//wykonywany jest inny wątek
//wykonywany jest inny wątek
System.out.print(" ");
System.out.print("15");
//wykonywany jest inny wątek
//wykonywany jest inny wątek
System.out.print(" ");
System.out.print("lat");
System.out.println();
//wykonywany jest inny wątek
//wykonywany jest inny wątek
//wykonywany jest inny wątek
System.out.print("Lena ma");
System.out.print(" ");
//wykonywany jest inny wątek
//wykonywany jest inny wątek
System.out.print("21");
System.out.print(" ");
//wykonywany jest inny wątek
//wykonywany jest inny wątek
//wykonywany jest inny wątek
System.out.print("lat");
System.out.println();
Co naprawdę jest wyświetlane
Nick ma Lena ma  15 21 lat
lat

A oto inny przykład:

Kod Opis
class MyClass
{
private String name1 = "Ally";
private String name2 = "Lena";
public void swap()
{
String s = name1;
name1 = name2;
name2 = s;
}
}
Metoda swap zamienia wartości zmiennych name1 i name2.

Co może się zdarzyć, jeśli zostanie ona wywołana z dwóch wątków jednocześnie?

Rzeczywiste wykonanie kodu Kod pierwszego wątku Kod drugiego wątku
String s1 = name1; //Ally
name1 = name2; //Lena
String s2 = name1; //Lena(!)
name1 = name2; //Lena
name2 = s1; //Ally
name2 = s2; //Lena
String s1 = name1;
name1 = name2;
//wykonywany jest inny wątek
//wykonywany jest inny wątek
name2 = s1;
//wykonywany jest inny wątek
//wykonywany jest inny wątek
//wykonywany jest inny wątek
String s2 = name1;
name1 = name2;
//wykonywany jest inny wątek
name2 = s2;
Konkluzja
Obie zmienne mają wartość «Lena».
Obiekt «Ally» nie przetrwał. Straciliśmy go.

– Kto by przypuszczał, że takie błędy są możliwe przy tak prostej operacji przypisania?!

– Tak, ale istnieje rozwiązanie tego problemu. Jednak porozmawiamy o tym trochę później - zaschło mi w gardle.