Wątki w Javie – programowanie współbieżne

Pojęcie wielowątkowości jest od dawna znane użytkownikom komputerów. Od wielu lat na rynku występują procesory wielordzeniowe, które pozwalają na pracę kilku procesów systemowych na raz. Jednakże w czasach, kiedy nie były one standardem również istniała współbieżność – była imitowana przez wywłaszczanie procesów i przyznawanie czasu pracy procesora innemu.

Procesy

Proces jest elementem systemu operacyjnego, który służy do organizacji wykonywania programu tak, aby dostępne były odpowiednie zasoby np. procesor, pamięć. Ważną cechą jest to, że każdy proces posiada swoje dane, identyfikator (PID). W związku z tym w przypadku aplikacji wieloprocesorowej wymagana jest komunikacja między nimi.

Wątki

Wątki w odróżnieniu od procesów są dużo lżejsze i są ich częścią. Proces składa się z co najmniej jednego wątku, ale może być ich więcej.

Główną różnicą jest to, że zasoby są współdzielone między wątkami w wewnątrz jednego procesu. Daje to rozmaite korzyści np. skrócenie czasu oczekiwania na wykonanie zadań aplikacji. Wielowątkowość nie jest jednak pozbawiona wad i jedną z nich jest problem spójności danych. Aby temu zapobiec należy synchronizować pracę wątków, co może przysporzyć sporo problemów.

Uruchomienie kilku wątków jest niezbędne w przypadku bardziej złożonych aplikacji. Wyobraźmy sobie aplikację typu przeglądarka internetowa. Zazwyczaj użytkownicy przeglądają różne strony internetowe, pobierają pliki, słuchają muzyki (z karty grającej w tle). Dzieje się tak dlatego, że przeglądarka jest aplikacją wielowątkową. Gdyby było inaczej nawet ściąganie plików zablokowałoby jakąkolwiek możliwość ingerencji w działanie programu – nawet zamknięcia aplikacji.

Tworzenie wątków

Technicznie tworzenie nowych wątków jest prostym zadaniem. Musimy wykonać jedynie kilka czynności.

Utworzenie klasy

Zaczynamy od utworzenia prostej klasy implementującej interfejs Runnable.

Uruchomienie wątków

Gdy mamy już klasę implementującą interfejs Runnable wystarczy utworzyć obiekty, a następnie przy ich pomocy obiekty klasy Thread. Następnie uruchomić wątek przy pomocy metody start().

Przykładowy program

Wyobraźmy sobie program, który posiada kilka funkcjonalności i każda do swojego wykonania zajmuje określoną ilość czasu procesora. W naszym przykładzie będą to czasy 1000, 500 i 100 ms. Ma to na celu pokazanie, że różne procedury mogą wymagać innej ilości czasu do wykonania. Równie dobrze pierwsza operacja mogłaby wymagać znacznie więcej czasu, a dwie pozostałe tyle samo. Dzięki zastosowaniu wątków procesor może obsłużyć te krótkie operacje wcześniej i nie trzeba czekać na zakończenie tej pierwszej – najdłuższej. Ma to znaczenie w przypadku graficznego interfejsu użytkownika, gdy nie może on wcisnąć przycisku zatrzymania operacji.

W klasie ThreadTest będziemy przechowywać nr wątku oraz czas wykonywania operacji w nim zawartej. W rzeczywistości żaden wątek nic nie będzie robił. Oczekiwanie ma za zadanie symulację wykonywania jakiegoś algorytmu. Wykonamy je metodą sleep() z klasy Thread.

Jak widać nie trzeba bezpośrednio wywoływać metody run() – jest ona uruchamiana po wywołaniu metody start(). Przykładowym wynikiem uruchomienia programu może być następująca.

Wątek nr 1, który trwa najdłużej nie blokuje programu i równolegle wykonują się pozostałe wątki. Następnie czeka znowu na czas procesora i wykonuje się ponownie.