Programowanie w Javie polega w zasadzie na określaniu instrukcji, które mają się kolejno wykonywać, jedna po drugiej, od góry do dołu, w kolejności umieszczenia w pliku kodu źródłowego. My, programując, określamy jakie to mają być instrukcje, a coś – w przypadku Javy będzie to Wirtualna Maszyna Javy -, te instrukcje wykonuje.
Pierwszą instrukcją programu jest pierwsza instrukcja metody głównej main(…) a ostatnią jej instrukcja ostatnia. Oczywiście będziemy z metody main(…) wywoływali różne inne metody (tej samej lub innych klas), ale nadal wszystko będzie wykonywane w ustalonej kolejności, instrukcja po instrukcji. Wszystkie instrukcje naszego programu będą wykonane w jednej sekwencji. Mówiąc językiem programowania współbieżnego – w jednym wątku.
Jednak w świecie rzeczywistym wiele rzeczy dzieje się naraz, niezależnie od siebie. Wiele rzeczy dzieje się naraz w świecie rzeczywistym i wiele rzeczy potrzebuje się dziać naraz, niezależnie od siebie, w aplikacjach które w tym świecie funkcjonują. Nowoczesne języki programowania, w tym Java, umożliwiają tworzenie takich wielowątkowych aplikacji. W aplikacjach tych określamy już nie jedną sekwencję instrukcji, ale kilka, dowolnie wiele, z których każda może wykonywać się niezależnie od siebie, nijako w tym samym czasie, tzn. współbieżnie.
Ciąg instrukcji zapisanych w metodzie main(…) definiuje instrukcje, które będą wykonane w ramach tzw. głównego wątku. Kod metody main(…) to definicja wątku, który to wątek uruchamiany jest wraz z uruchomieniem programu. Uruchomienie programu to de facto uruchomienie tego głównego wątku.
Jeśli chcemy aby jakiś inny kod (ciąg instrukcji) wykonywał się niezależnie od kodu wątku głównego, tj. współbieżnie z nim, to po pierwsze musimy określić jaki to kod, a po drugie musimy go uruchomić. Kod wątku głównego określamy implementując metodę main(…); kod innych wątków implementując dowolną klasę dziedziczącą z klasy java.lang.Thread albo klasę która implementuje interfejs java.lang.Runnable. Mamy więc dwie możliwości, jednak w obydwu przypadkach implementacja wątku sprowadza się do implementacji metody run().
Aby zdefiniować wątek – którego jedyną instrukcją będzie wyświetlenie tekstu powitalnego – poprzez implementację klasy dziedziczącej z klasy Thread moglibyśmy napisać:
class MyThread extends Thread { public void run() { System.out.println("Witaj w świecie programowania współbieżnego!"); } }
Klasa MyThread dziedziczy z klasy Thread, tak więc jest definicją wątku, ale samo zaimplementowanie tej klasy jeszcze nie powoduje że kod ten się wykona. Uruchamiając program uruchamiamy tylko wątek główny, tj. kod zapisany w metodzie main(…), a żeby uruchomić wątek zaimplementowany w postaci klasy MyThread musimy utworzyć instancję tej klasy i uruchomić dla niej metodę start(). Kod który to zrobi musimy naturalnie umieścić pośród instrukcji wątku głównego (albo jakiegoś innego wątku uruchomionego z wątku głównego). Moglibyśmy więc napisać:
public class TestClass { public static void main(String[] args) { Thread newThread = new MyThread(); newThread.start(); } }
Metoda start() odziedziczona przez klasę MyThread z klasy Thread tworzy nowy wątek Wirtualnej Maszyny Javy i uruchamia w jego ramach kod zdefiniowany w metodzie run(). Od tej chwili mamy dwa aktywne, współbieżnie wykonujące się ciągi instrukcji, tzn. wątki – wątek główny (który nic nie robi poza uruchomieniem kolejnego wątku) oraz wątek zdefiniowany poprzez klasę MyThread.
W świecie nic nie dzieje się niezależnie od siebie:). Jednak modelując tylko jego mały fragment, czasem wygodnie jest zrobić takie założenie.