Cześć! Dzisiaj będziemy nadal mówić o wielowątkowości. Przyjrzyjmy się klasie Thread i działaniu kilku jej metod. Kiedy wcześniej studiowaliśmy metody klasowe, zwykle pisaliśmy po prostu tak: <nazwa metody> -> <co robi metoda>.
To nie zadziała z
Przypomnijmy przykład z poprzedniej lekcji:
Metoda
Metoda

Thread
metodami '' :) Mają bardziej złożoną logikę, której nie będziesz w stanie zrozumieć bez kilku przykładów.
Metoda Thread.start().
Zacznijmy od powtórzenia się. Jak zapewne pamiętasz, możesz utworzyć wątek, sprawiając, że twoja klasa dziedziczy klasęThread
i nadpisuje run()
metodę. Ale oczywiście sam się nie uruchomi. W tym celu wywołujemy start()
metodę naszego obiektu. 
public class MyFirstThread extends Thread {
@Override
public void run() {
System.out.println("Thread executed: " + getName());
}
}
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.start();
}
}
}
Uwaga: Aby rozpocząć wątek, musisz wywołaćstart()
metodę specjalną, a nierun()
metodę! Jest to błąd, który łatwo popełnić, zwłaszcza gdy po raz pierwszy zaczynasz uczyć się wielowątkowości. W naszym przykładzie, jeśli wywołaszrun()
metodę 10 razy zamiaststart()
, otrzymasz to:
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
MyFirstThread thread = new MyFirstThread();
thread.run();
}
}
}
Spójrz na wyniki naszego programu: Wątek wykonany: Wątek-0 Wątek wykonany: Wątek-1 Wątek wykonany: Wątek-2 Wątek wykonany: Wątek-3 Wątek wykonany: Wątek-4 Wątek wykonany: Wątek-5 Wątek wykonany: Wątek-6 Wątek wykonany: Wątek-7 Wątek wykonany: Wątek-8 Wątek wykonany: Wątek-9 Spójrz na kolejność wyjścia: Wszystko dzieje się w idealnej kolejności. Dziwne, co? Nie jesteśmy do tego przyzwyczajeni, ponieważ wiemy już, że kolejność uruchamiania i wykonywania wątków jest określana przez nadrzędny intelekt w naszym systemie operacyjnym: harmonogram wątków. Może po prostu mieliśmy szczęście? Oczywiście nie chodzi tu o szczęście. Możesz to sprawdzić, uruchamiając program jeszcze kilka razy. Problem polega na tym, że wywołanierun()
metoda bezpośrednio nie ma nic wspólnego z wielowątkowością. W takim przypadku program zostanie wykonany w głównym wątku, tym samym wątku, który wykonuje metodę main()
. Po prostu kolejno wypisuje 10 linii na konsoli i to wszystko. 10 wątków nie zostało rozpoczętych. Pamiętaj więc o tym na przyszłość i stale się sprawdzaj. Jeśli chcesz, aby run()
metoda została wywołana, wywołaj start()
. Idźmy dalej.
Metoda Thread.sleep().
Aby na chwilę zawiesić wykonywanie bieżącego wątku, używamysleep()
metody. 
sleep()
przyjmuje jako argument liczbę milisekund, która wskazuje czas uśpienia wątku.
public class Main {
public static void main(String[] args) throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println(" - How long did I sleep? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " seconds");
}
}
Wyjście konsoli: - Jak długo spałem? - 3 sekundy Uwaga: metoda sleep()
jest statyczna: uśpi bieżący wątek. To znaczy ten, który jest obecnie wykonywany. Oto kolejna ważna kwestia: uśpiona nić może zostać przerwana. W takim przypadku program wyrzuca plik InterruptedException
. Rozważymy przykład poniżej. A propos, co się dzieje po przebudzeniu wątku? Czy nadal będzie wykonywany od miejsca, w którym został przerwany? Nie. Po przebudzeniu wątku, tj. czasie, który Thread.sleep()
minął jako argument do, przechodzi on do stanu wykonalnegopaństwo. Ale to nie znaczy, że program do planowania wątków go uruchomi. Całkiem możliwe, że da pierwszeństwo jakiemuś nieuśpionemu wątkowi i pozwoli naszemu świeżo obudzonemu wątkowi kontynuować pracę nieco później. Pamiętaj o tym: przebudzenie nie oznacza natychmiastowego kontynuowania pracy!
Metoda Thread.join().

join()
wstrzymuje wykonywanie bieżącego wątku do czasu zakończenia innego wątku. Jeśli mamy 2 wątki t1
i t2
, i piszemy
t1.join()
następnie t2
nie rozpocznie się, dopóki nie t1
zakończy swojej pracy. Metodę join()
można wykorzystać do zagwarantowania kolejności wykonywania wątków. Rozważmy, jak join()
działa metoda w następującym przykładzie:
public class ThreadExample extends Thread {
@Override
public void run() {
System.out.println("Thread started: " + getName());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread " + getName() + " is finished.");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadExample t1 = new ThreadExample();
ThreadExample t2 = new ThreadExample();
t1.start();
/* The second thread (t2) will start running only after the first thread (t1)
is finished (or an exception is thrown) */
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
// The main thread will continue running only after t1 and t2 have finished
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("All threads have finished. The program is finished.");
}
}
Stworzyliśmy prostą ThreadExample
klasę. Jego zadaniem jest wyświetlenie komunikatu, że wątek został uruchomiony, zasypianie na 5 sekund, a na koniec zgłoszenie, że praca została zakończona. Bułka z masłem. Główna logika jest w Main
klasie. Spójrz na komentarze: używamy tej join()
metody do skutecznego zarządzania kolejnością wykonywania wątków. Jeśli pamiętasz, jak rozpoczęliśmy ten temat, kolejność wykonywania jest obsługiwana przez program do planowania wątków. Prowadzi wątki według własnego uznania: za każdym razem w inny sposób. Tutaj używamy metody, aby zagwarantować, że t1
wątek zostanie najpierw uruchomiony i wykonany jako pierwszy, a następniet2
wątek i dopiero potem główny wątek programu będzie kontynuowany. Iść dalej. W rzeczywistych programach często znajdziesz sytuacje, w których będziesz musiał przerwać wykonywanie wątku. Na przykład nasz wątek działa, ale oczekuje na określone zdarzenie lub warunek. Jeśli tak się stanie, wątek zatrzymuje się. Pewnie miałoby to sens, gdyby istniała jakaś stop()
metoda. Ale to nie takie proste. Dawno, dawno temu Java rzeczywiście miała Thread.stop()
metodę i pozwalała na przerwanie wątku. Ale później został usunięty z biblioteki Java. Możesz go znaleźć w dokumentacji Oracle i zobaczyć, że jest oznaczony jako przestarzały. Dlaczego? Ponieważ po prostu zatrzymał wątek, nie robiąc nic więcej. Na przykład wątek może pracować z danymi i coś zmieniać. Następnie w połowie swojej pracy został nagle i bezceremonialnie odcięty metodą stop()
. Bez odpowiedniego wyłączenia, zwolnienia zasobów, nawet obsługi błędów — nic z tego nie mogło się wydarzyć. Lekko przesadzając, stop()
metoda po prostu niszczyła wszystko na swojej drodze. To było jak wyciągnięcie przewodu zasilającego z gniazdka w celu wyłączenia komputera. Tak, możesz uzyskać pożądany rezultat. Ale wszyscy wiedzą, że po kilku tygodniach komputer nie będzie ci dziękował za takie traktowanie. Dlatego logika przerywania wątków zmieniła się w Javie i teraz używa specjalnej interrupt()
metody.
Metoda Thread.interrupt().
Co się stanie, jeśliinterrupt()
metoda zostanie wywołana w wątku? Istnieją 2 możliwości:
- Jeśli obiekt był w stanie oczekiwania, na przykład z powodu metod
join
lubsleep
, to oczekiwanie zostanie przerwane i program zgłosi błądInterruptedException
. - Jeśli wątek był w stanie funkcjonalnym, wówczas
interrupted
na obiekcie zostanie ustawiona flaga logiczna.
Thread
klasa ma boolean isInterrupted()
metodę. Wróćmy do przykładu z zegarem, który był na lekcji w kursie podstawowym. Dla wygody nieco go uprościliśmy:
public class Clock extends Thread {
public static void main(String[] args) throws InterruptedException {
Clock clock = new Clock();
clock.start();
Thread.sleep(10000);
clock.interrupt();
}
public void run() {
Thread current = Thread.currentThread();
while (!current.isInterrupted())
{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("The thread was interrupted");
break;
}
System.out.println("Tick");
}
}
}
W takim przypadku zegar jest uruchamiany i zaczyna tykać co sekundę. W 10. sekundzie przerywamy wątek zegara. Jak już wiesz, jeśli wątek, który próbujemy przerwać, znajduje się w jednym ze stanów oczekiwania, wynikiem jest plik InterruptedException
. Jest to sprawdzony wyjątek, więc możemy go łatwo złapać i wykonać naszą logikę, aby zakończyć program. I to właśnie zrobiliśmy. Oto nasz wynik: Tick Tick Tick Tcik Tick Tick Tick Tick Tick Wątek został przerwany Na tym kończy się nasze wprowadzenie do Thread
najważniejszych metod klasy. Powodzenia!
GO TO FULL VERSION