JavaRush /Blog Java /Random-PL /Wielowątkowość: co robią metody klas wątków

Wielowątkowość: co robią metody klas wątków

Opublikowano w grupie Random-PL
Cześć! Dzisiaj kontynuujemy dyskusję na temat wielowątkowości. Przyjrzyjmy się klasie Thread i działaniu kilku jej metod. Wcześniej, gdy studiowaliśmy metody klasowe, najczęściej zapisywaliśmy to po prostu w ten sposób: „nazwa metody” -> „co robi”.
Wielowątkowość: co robią metody klasy Thread - 1
To nie zadziała z metodami Thread :) Ich logika jest bardziej skomplikowana i bez kilku przykładów nie da się tego zrozumieć.

Metoda Thread.start().

Zacznijmy od powtórzeń. Jak zapewne pamiętasz, możesz utworzyć wątek, dziedzicząc swoją klasę z klasy Threadi zastępując znajdującą się w niej metodę run(). Ale oczywiście nie zacznie się samo. W tym celu wywołujemy metodę na naszym obiekcie start(). Wielowątkowość: co robią metody klasy Thread - 2Przypomnijmy przykład z poprzedniego wykładu:
public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Выполнен поток " + 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ć metodę specjalnąstart(), a nierun()! Łatwo jest popełnić ten błąd, zwłaszcza gdy po raz pierwszy uczymy się wielowątkowości. Jeśli w naszym przykładzie wywołasz metodę na obiekcie 10 razyrun()zamiaststart(), wynik będzie następujący:
public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {
           MyFirstThread thread = new MyFirstThread();
           thread.run();
       }
   }
}
Wątek wątek-0 wykonany Wątek-1 wykonany Wątek-7 wykonany Wątek wątek-3 wykonany Wątek-4 wykonany Wątek wątek-5 wykonany Wątek-6 wykonany Wątek-7 wykonany Wątek-8 wykonany Wątek-9 wykonany Spójrz na sekwencję wyjściową: wszystko idzie ściśle w porządku. Dziwne, prawda? Nie jesteśmy do tego przyzwyczajeni, bo już wiemy, że o kolejności uruchamiania i wykonywania wątków decyduje superinteligencja naszego systemu operacyjnego – harmonogram wątków. Może po prostu miałem szczęście? Oczywiście nie jest to kwestia szczęścia. Możesz to sprawdzić, uruchamiając program jeszcze kilka razy. Rzecz w tym, że bezpośrednie wywołanie metodyrun()nie ma nic wspólnego z wielowątkowością. W tym przypadku program zostanie wykonany w wątku głównym – tym, w którym wykonywana jest metodamain(). Po prostu wyśle ​​sekwencyjnie 10 linii do konsoli i to wszystko. Nie zostanie uruchomionych 10 wątków. Dlatego pamiętajcie na przyszłość i stale sprawdzajcie siebie. Jeśli chcesz, żeby to zostało zrobionerun(), zadzwoństart(). Przejdźmy dalej.

Metoda Thread.sleep().

Aby na jakiś czas wstrzymać wykonywanie bieżącego wątku, użyj metody sleep(). Wielowątkowość: co robią metody klasy Thread - 3Metoda sleep()przyjmuje jako parametr liczbę milisekund, czyli czas, na który wątek musi zostać uśpiony.
public class Main {

   public static void main(String[] args) throws InterruptedException {

       long start = System.currentTimeMillis();

       Thread.sleep(3000);

       System.out.println(" - Сколько я проспал? \n - " + ((System.currentTimeMillis()-start)) / 1000 + " секунды");

   }
}
Dane wyjściowe konsoli: - Jak długo spałem? - 3 sekundy Uwaga: metoda sleep()jest statyczna: usypia bieżący wątek. To znaczy ten, który w tej chwili działa. Kolejny ważny niuans: przepływ w stanie uśpienia może zostać przerwany. W takim przypadku w programie wystąpi wyjątek InterruptedException. Przyjrzymy się przykładowi poniżej. Swoją drogą, co się dzieje, gdy wątek się „obudzi”? Czy natychmiast będzie kontynuował wykonywanie od miejsca, w którym przerwał? NIE. Po przebudzeniu wątek — gdy upłynie czas podany jako argument Thread.sleep()— przechodzi w stan umożliwiający wykonanie . Nie oznacza to jednak, że planista wątków go uruchomi. Jest całkiem możliwe, że preferuje inny „nieśpiący” wątek, a nasz „świeżo przebudzony” wątek będzie kontynuował działanie nieco później. Pamiętaj: „przebudzenie nie oznacza kontynuowania pracy w tej samej sekundzie!”

Metoda Thread.join().

Многопоточность: что делают методы класса Thread - 4Metoda join()zawiesza wykonywanie bieżącego wątku do czasu zakończenia innego wątku. Jeśli mamy 2 wątki, t1i t2, i piszemy -
t1.join()
t2nie rozpocznie pracy, dopóki t1 nie zakończy swojej pracy. Metodę join()można zastosować do zapewnienia kolejności wykonywania wątków. Spójrzmy na pracę join()na przykładzie:
public class ThreadExample extends Thread {

   @Override
   public void run() {

       System.out.println("Начало работы потока " + getName());

       try {
           Thread.sleep(5000);
       } catch (InterruptedException e) {
           e.printStackTrace();
       }
       System.out.println("Поток " + getName() +  " завершил работу.");
   }
}


public class Main {

   public static void main(String[] args) throws InterruptedException {

       ThreadExample t1 = new ThreadExample();
       ThreadExample t2 = new ThreadExample();

       t1.start();


 /*Второй поток t2 начнет выполнение только после того, Jak будет завершен
       (Lub бросит исключение) первый поток - t1*/
       try {
           t1.join();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       t2.start();

       //Главный поток продолжит работу только после того, Jak t1 и t2 завершат работу
       try {
           t1.join();
           t2.join();
       } catch (InterruptedException e) {
           e.printStackTrace();
       }

       System.out.println("Все потоки закончLub работу, программа завершена");

   }
}
Stworzyliśmy prostą klasę ThreadExample. Jego zadaniem jest wyświetlenie na ekranie komunikatu o rozpoczęciu pracy, następnie zaśnięcie na 5 sekund i na koniec poinformowanie o zakończeniu pracy. Nic skomplikowanego. Główna logika zawarta jest w klasie Main. Spójrz na komentarze: stosując tę ​​metodę, join()z powodzeniem kontrolujemy kolejność wykonywania wątków. Jeśli pamiętasz początek tematu, zrobił to planista wątków. Uruchamiał je według własnego uznania: za każdym razem inaczej. Tutaj stosując metodę zapewniliśmy, że najpierw zostanie uruchomiony i wykonany wątek t1, następnie wątek t2, a dopiero po nich główny wątek wykonania programu. Zacząć robić. W prawdziwych programach często spotkasz się z sytuacjami, w których będziesz musiał przerwać wykonywanie jakiegoś wątku. Na przykład nasz wątek działa, ale czeka na spełnienie określonego zdarzenia lub warunku. Jeśli tak się stanie, zatrzyma się. Prawdopodobnie byłoby logiczne, gdyby istniała jakaś metoda taka jak stop(). Jednak nie wszystko jest takie proste. Dawno, dawno temu Thread.stop()istniała metoda w Javie, która pozwalała przerwać pracę wątku. Ale później został usunięty z biblioteki Java. Możesz sprawdzić to w dokumentacji Oracle i zobaczyć, że jest oznaczone jako przestarzałe . Dlaczego? Ponieważ po prostu zatrzymywał przepływ bez dodatkowej pracy. Na przykład wątek może pracować z danymi i coś w nich zmieniać. Potem został nagle znokautowany stop()w trakcie pracy – i tyle. Nie doszło do prawidłowego zamknięcia, zwolnienia zasobów, nawet obsługi błędów – nic takiego się nie wydarzyło. Metoda stop(), mówiąc przesadnie, po prostu niszczyła wszystko na swojej drodze. Jego działanie można porównać do tego, jak ktoś wyciąga wtyczkę z gniazdka, aby wyłączyć komputer. Tak, możesz osiągnąć pożądany rezultat. Ale wszyscy rozumieją, że za kilka tygodni komputer nie powie za to „dziękuję”. Z tego powodu zmieniono logikę przerywania wątków w Javie i obecnie stosowana jest specjalna metoda - interrupt().

Metoda Thread.interrupt().

Co się stanie, jeśli wywołasz metodę interrupt() w wątku ? Istnieją 2 opcje:
  1. Jeżeli obiekt w tym momencie znajdował się w stanie oczekiwania np. joinlub sleep, oczekiwanie zostanie przerwane i program wyrzuci InterruptedException.
  2. Jeśli w tym momencie wątek był w stanie roboczym, ustawiona zostanie flaga boolowska obiektu interrupted.
Ale będziemy musieli sprawdzić obiekt pod kątem wartości tej flagi i sami poprawnie wykonać pracę! W tym celu klasa Threadposiada specjalną metodę - boolean isInterrupted(). Wróćmy do przykładu zegara z wykładu z kursu głównego. Dla wygody jest to nieco uproszczone:
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("Работа потока была прервана");
               break;
           }
           System.out.println("Tik");
       }
   }
}
W naszym przypadku zegar zaczyna tykać i zaczyna tykać co sekundę. W 10. sekundzie przerywamy bieg zegara. Jak już wiesz, jeśli wątek, który chcemy przerwać, znajduje się w jednym ze stanów oczekiwania, skutkuje to InterruptedException. Ten typ wyjątku jest wyjątkiem sprawdzanym, więc można go łatwo przechwycić i wykonać naszą logikę zakończenia programu. To właśnie zrobiliśmy. Oto nasz wynik: Tik Tik Tik Tik Tik Tik Tik Tik Tik Tik Praca wątku została przerwana. Na tym kończymy nasze wprowadzenie do głównych metod zajęć Thread. Aby utrwalić swoją wiedzę, możesz obejrzeć ten wykład wideo na temat wielowątkowości:
posłuży jako doskonały materiał dodatkowy! Na koniec, po przeglądzie metod, mówi dokładnie, przez co będziemy dalej przechodzić w trakcie kursu :) Powodzenia!
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION