Definiowanie wątków poprzez implementację podklas klasy java.lang.Thread – tak jak to zrobiliśmy w artykule „Programowanie współbieżne” – jest najprostszą możliwą metodą, jednak nie zawsze najwygodniejszą. Alternatywnie, wątek możemy zdefiniować implementując interfejs java.lang.Runnable i na podstawie takiej klasy tworząc instancje klasy Thread, które to instancje są koniec końców tak czy inaczej potrzebne – aby wątek uruchomić.
Ekwiwalentem klasy MyThread której kod znajdziemy w artykule „Programowanie współbieżne”, która to klasa implementuje wątek rozszerzając klasę Thread jest klasa implementująca interfejs Runnable:
class MyRunnable implements Runnable {
public void run() {
System.out.println("Witaj w świecie programowania współbieżnego!");
}
}
Aby tak zdefiniowany wątek uruchomić potrzebujemy utworzyć instancję klasy MyRunnable i następnie na podstawie tej instancji instancję klasy Thread. Wątek, niezależnie od sposobu implementacji uruchamiamy w ten sam sposób, tj. wywołując metodę start(). Aby uruchomić wątek zdefiniowany w postaci klasy MyRunnable moglibyśmy napisać:
public class TestClass {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread newThread = new Thread(runnable);
newThread.start();
}
}
Pójdźmy teraz o krok dalej, i uruchommy kilka wątków. Każda instancja klasy Thread lub jej podklasy odpowiada jednemu wątkowi, żeby więc uruchomić kilka wątków będziemy musieli utworzyć kilka instancji. Chcąc uruchomić kilka wątków zdefiniowanych w postaci klasy MyThread moglibyśmy napisać:
public class TestClass {
public static void main(String[] args) {
Thread threadA = new MyThread();
Thread threadB = new MyThread();
Thread threadC = new MyThread();
threadA.start();
threadB.start();
threadC.start();
}
}
Aby uruchomić trzy wątki zdefiniowane w postaci klasy MyRunnable możemy postąpić dwojako. Możemy napisać tak:
public class TestClassA {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread threadA = new Thread(runnable);
Thread threadB = new Thread(runnable);
Thread threadC = new Thread(runnable);
threadA.start();
threadB.start();
threadC.start();
}
}
albo tak:
public class TestClassB {
public static void main(String[] args) {
Thread threadA = new Thread(new MyRunnable());
Thread threadB = new Thread(new MyRunnable());
Thread threadC = new Thread(new MyRunnable());
threadA.start();
threadB.start();
threadC.start();
}
}
Wbrew pozorom, dwie pokazane powyżej klasy, tj. klasy TestClassA i TestClassB, różnią się od siebie diametralnie. TestClassB to odpowiednik klasy TestClass z poprzedniego przykładu. Zarówno kod klasy TestClassB jak i klasy TestClass tworzy trzy instancje klasy definiującej kod wykonywany przez wątek i uruchamia trzy wątki, po jednym dla każdej instancji. Kod klasy TestClassA dla odmiany tworzy tylko jedną instancję klasy definiującej kod wykonywany przez wszystkie trzy wątki. Co prawda tworzymy w klasie TestClassA trzy instancje klasy Thread, jednak to nie klasa Thread definiuje kod wykonywany przez wątki, tylko klasa MyRunnable, której instancję mamy jedną – wszystkie trzy instancje klasy Thread zostały utworzone na bazie tej samej instancji klasy MyRunnable.
To, czy kilka wątków wykonuje współbieżnie ten sam kod, czy każdy z nich swoją kopię takiego samego kodu ma zasadnicze znaczenie. Przekonajmy się o tym na przykładzie. Zmieńmy implementację klasy MyRunnable na pokazaną poniżej:
class MyRunnable implements Runnable {
int val = 0;
public void run() {
val = val + 1;
System.out.println(val);
}
}
Uruchommy teraz klasę testującą TestClassA. Wynikiem będzie zapewne ciąg liczb 1, 2, 3 (choć niekoniecznie tak być musi, ale o tym w następnych artykułach). Uruchommy też klasę TestClassB. Wynikiem zawsze będzie ciąg liczb 1, 1, 1 (tak, trzy razy liczba 1). Dlaczego tak się dzieje? To wyjaśniam w następnym odcinku kursu, odcinku „Wykonanie wielowątkowe”.



Kurs języka Java

