Każda aplikacja może mieć wiele procesów (instancji). Każdy z tych procesów można przypisać jako pojedynczy wątek lub wiele wątków. W tym samouczku zobaczymy, jak wykonywać wiele zadań jednocześnie, a także dowiemy się więcej o wątkach i synchronizacji między wątkami.
W tym samouczku nauczymy się:
- Co to jest pojedynczy wątek
- Co to jest wielowątkowość w Javie?
- Cykl życia wątku w Javie
- Synchronizacja wątków Java
- Przykład wielowątkowości w Javie
Co to jest pojedynczy wątek?
Pojedynczy wątek jest w zasadzie lekką i najmniejszą jednostką przetwarzania. Java używa wątków przy użyciu „klasy wątku”.
Istnieją dwa typy wątków - wątek użytkownika i wątek demona (wątki demona są używane, gdy chcemy wyczyścić aplikację i są używane w tle).
Gdy aplikacja jest uruchamiana po raz pierwszy, tworzony jest wątek użytkownika. Po tym, możemy tworzyć wiele wątków użytkowników i wątków demonów.
Przykład pojedynczego wątku:
pakiet demotest;klasa publiczna GuruThread{public static void main (String [] args) {System.out.println ("Pojedynczy wątek");}}
Zalety pojedynczego gwintu:
- Zmniejsza obciążenie aplikacji, ponieważ pojedynczy wątek jest wykonywany w systemie
- Zmniejsza również koszty utrzymania aplikacji.
Co to jest wielowątkowość w Javie?
MULTITHREADING w Javie to proces wykonywania dwóch lub więcej wątków jednocześnie w celu maksymalnego wykorzystania procesora. Aplikacje wielowątkowe wykonują dwa lub więcej wątków jednocześnie. Dlatego jest również znany jako Współbieżność w Javie. Każdy wątek biegnie równolegle do siebie. Wiele wątków nie przydziela oddzielnego obszaru pamięci, dlatego oszczędza pamięć. Ponadto przełączanie kontekstu między wątkami zajmuje mniej czasu.
Przykład wielu wątków:
pakiet demotest;publiczna klasa GuruThread1 implementuje Runnable{public static void main (String [] args) {Wątek guruThread1 = nowy wątek ("Guru1");Wątek guruThread2 = nowy wątek („Guru2”);guruThread1.start ();guruThread2.start ();System.out.println ("Nazwy wątków są następujące:");System.out.println (guruThread1.getName ());System.out.println (guruThread2.getName ());}@Nadpisaniepublic void run () {}}
Zalety wielowątkowości:
- Użytkownicy nie są blokowani, ponieważ wątki są niezależne i możemy jednocześnie wykonywać wiele operacji
- W związku z tym wątki są niezależne, inne wątki nie zostaną dotknięte, jeśli jeden wątek napotka wyjątek.
Cykl życia wątku w Javie
Cykl życia wątku:
Istnieją różne etapy cyklu życia nici, jak pokazano na powyższym schemacie:
- Nowy
- Runnable
- Bieganie
- Czekanie
- Nie żyje
- Nowość: W tej fazie, wątek jest tworzony przy użyciu klasy „klasy wątku” pozostaje .it w tym stanie aż program rozpoczyna wątek. Jest również znany jako zrodzony wątek.
- Runnable: na tej stronie instancja wątku jest wywoływana za pomocą metody startowej. Kontrola wątku jest przekazywana do harmonogramu w celu zakończenia wykonywania. To zależy od harmonogramu, czy uruchomić wątek.
- Uruchomiony: gdy wątek zaczyna się wykonywać, stan zmienia się na „uruchomiony”. Harmonogram wybiera jeden wątek z puli wątków i rozpoczyna wykonywanie w aplikacji.
- Oczekiwanie: jest to stan, w którym wątek musi czekać. Ponieważ w aplikacji działa wiele wątków, istnieje potrzeba synchronizacji między wątkami. W związku z tym jeden wątek musi czekać, aż drugi wątek zostanie wykonany. Dlatego stan ten nazywany jest stanem oczekiwania.
- Martwy: jest to stan, w którym wątek jest zakończony. Wątek jest uruchomiony i po zakończeniu przetwarzania znajduje się w stanie „martwym”.
Niektóre z powszechnie używanych metod dla wątków to:
metoda | Opis |
---|---|
początek() | Ta metoda rozpoczyna wykonywanie wątku, a maszyna JVM wywołuje metodę run () w wątku. |
Uśpienie (w milisekundach int) | Ta metoda powoduje uśpienie wątku, dlatego wykonanie wątku zostanie wstrzymane na podane milisekundy, a następnie ponownie rozpocznie się wykonywanie wątku. Ta pomoc w synchronizacji wątków. |
getName () | Zwraca nazwę wątku. |
setPriority (int newpriority) | Zmienia priorytet wątku. |
wydajność () | Powoduje zatrzymanie bieżącego wątku i wykonywanie innych wątków. |
Przykład: W tym przykładzie utworzymy wątek i zbadamy wbudowane metody dostępne dla wątków.
pakiet demotest;klasa publiczna thread_example1 implementuje Runnable {@Nadpisaniepublic void run () {}public static void main (String [] args) {Wątek guruthread1 = new Thread ();guruthread1.start ();próbować {guruthread1.sleep (1000);} catch (InterruptedException e) {// TODO Automatycznie wygenerowany blok catche.printStackTrace ();}guruthread1.setPriority (1);int gurupriority = guruthread1.getPriority ();System.out.println (gurupriority);System.out.println ("Wątek uruchomiony");}}
Wyjaśnienie kodu:
- Linia kodu 2: Tworzymy klasę „thread_Example1”, która implementuje interfejs Runnable (powinna być implementowana przez każdą klasę, której instancje mają być wykonywane przez wątek).
- Linia kodu 4: Zastępuje metodę uruchamiania uruchamialnego interfejsu, ponieważ jest obowiązkowe, aby przesłonić tę metodę
- Linia kodu 6: Tutaj zdefiniowaliśmy główną metodę, w której rozpoczniemy wykonywanie wątku.
- Linia kodu 7: Tutaj tworzymy nową nazwę wątku jako „guruthread1” przez utworzenie instancji nowej klasy wątku.
- Linia kodu 8: użyjemy metody "start" wątku używając instancji "guruthread1". Tutaj wątek zacznie się wykonywać.
- Linia kodu 10: Tutaj używamy metody "uśpienia" wątku używającego instancji "guruthread1". W związku z tym wątek będzie spał przez 1000 milisekund.
- Kod 9-14: Tutaj umieściliśmy metodę uśpienia w bloku try catch, ponieważ jest sprawdzany wyjątek, który występuje, np. Przerwany wyjątek.
- Linia kodu 15: Tutaj ustawiamy priorytet wątku na 1, niezależnie od tego, jaki był priorytet
- Linia kodu 16: Tutaj uzyskujemy priorytet wątku za pomocą metody getPriority ()
- Linia kodu 17: Tutaj wypisujemy wartość pobraną z getPriority
- Linia kodu 18: Tutaj piszemy tekst, w którym działa wątek.
Po wykonaniu powyższego kodu otrzymasz następujące dane wyjściowe:
Wynik:
5 to priorytet wątku, a Uruchomiony wątek to tekst, który jest wynikiem naszego kodu.
Synchronizacja wątków Java
W przypadku wielowątkowości programy zachowują się asynchronicznie. Jeśli jeden wątek zapisuje dane, a inny wątek odczytuje dane w tym samym czasie, może to spowodować niespójność w aplikacji.
Gdy istnieje potrzeba uzyskania dostępu do współdzielonych zasobów przez dwa lub więcej wątków, stosuje się podejście synchronizacyjne.
Java udostępnia zsynchronizowane metody implementacji zsynchronizowanego zachowania.
W tym podejściu, gdy wątek dotrze do zsynchronizowanego bloku, żaden inny wątek nie może wywołać tej metody na tym samym obiekcie. Wszystkie wątki muszą czekać, aż ten wątek zakończy synchronizowany blok i wyjdzie z tego.
W ten sposób synchronizacja pomaga w aplikacji wielowątkowej. Jeden wątek musi poczekać, aż inny wątek zakończy wykonywanie, tylko wtedy pozostałe wątki mogą zostać wykonane.
Można go zapisać w następującej formie:
Zsynchronizowano (obiekt){// Blok instrukcji do synchronizacji}
Przykład wielowątkowości w Javie
W tym przykładzie weźmiemy dwa wątki i pobierzemy nazwy wątku.
Przykład 1:
GuruThread1.javapakiet demotest;public class GuruThread1 implementuje Runnable {/ *** @param args* /public static void main (String [] args) {Wątek guruThread1 = nowy wątek ("Guru1");Wątek guruThread2 = nowy wątek („Guru2”);guruThread1.start ();guruThread2.start ();System.out.println ("Nazwy wątków są następujące:");System.out.println (guruThread1.getName ());System.out.println (guruThread2.getName ());}@Nadpisaniepublic void run () {}}
Wyjaśnienie kodu:
- Code Line 3: Wzięliśmy klasę „GuruThread1”, która implementuje Runnable (powinna być implementowana przez każdą klasę, której instancje mają być wykonywane przez wątek).
- Linia kodu 8: jest to główna metoda klasy
- Linia kodu 9: Tutaj tworzymy instancję klasy Thread, tworzymy instancję o nazwie „guruThread1” i tworzymy wątek.
- Linia kodu 10: Tutaj tworzymy instancję klasy Thread, tworzymy instancję o nazwie „guruThread2” i tworzymy wątek.
- Linia kodu 11: Rozpoczynamy wątek, czyli guruThread1.
- Linia kodu 12: Rozpoczynamy wątek, czyli guruThread2.
- Linia kodu 13: Wyprowadzanie tekstu jako „Nazwy wątków są następujące:”
- Linia kodu 14: Pobieranie nazwy wątku 1 za pomocą metody getName () klasy wątku.
- Linia kodu 15: Pobieranie nazwy wątku 2 przy użyciu metody getName () klasy wątku.
Po wykonaniu powyższego kodu otrzymasz następujące dane wyjściowe:
Wynik:
Nazwy wątków są tutaj wyprowadzane jako
- Guru1
- Guru2
Przykład 2:
W tym przykładzie nauczymy się przesłonić metody run () i start () uruchamialnego interfejsu oraz utworzymy dwa wątki tej klasy i odpowiednio je uruchomimy.
Bierzemy również udział w dwóch zajęciach
- Taki, który zaimplementuje uruchamialny interfejs i
- Kolejny, który będzie miał główną metodę i odpowiednio wykona.
pakiet demotest;public class GuruThread2 {public static void main (String [] args) {// TODO Automatycznie wygenerowany kod pośredniczący metodyGuruThread3 threadguru1 = nowy GuruThread3 ("guru1");threadguru1.start ();GuruThread3 threadguru2 = nowy GuruThread3 ("guru2");threadguru2.start ();}}class GuruThread3 implementuje Runnable {Thread guruthread;private String guruname;GuruThread3 (nazwa ciągu) {guruname = imię;}@Nadpisaniepublic void run () {System.out.println ("Wątek uruchomiony" + nazwa gurun);for (int i = 0; i <4; i ++) {System.out.println (i);System.out.println (nazwa gurun);próbować {Thread.sleep (1000);} catch (InterruptedException e) {System.out.println ("Wątek został przerwany");}}}public void start () {System.out.println ("Rozpoczęto wątek");if (guruthread == null) {guruthread = nowy wątek (this, guruname);guruthread.start ();}}}
Wyjaśnienie kodu:
- Code Line 2: Tutaj bierzemy klasę „GuruThread2”, która będzie zawierała główną metodę.
- Linia kodu 4: Tutaj bierzemy główną metodę klasy.
- Code Line 6-7: Tutaj tworzymy instancję klasy GuruThread3 (która jest tworzona w poniższych wierszach kodu) jako „threadguru1” i zaczynamy wątek.
- Code Line 8-9: Tutaj tworzymy kolejną instancję klasy GuruThread3 (która jest tworzona w poniższych wierszach kodu) jako „threadguru2” i zaczynamy wątek.
- Linia kodu 11: Tutaj tworzymy klasę „GuruThread3”, która implementuje uruchamialny interfejs (powinna być implementowana przez każdą klasę, której instancje mają być wykonywane przez wątek).
- Linia kodu 13-14: bierzemy dwie zmienne klasowe, z których jedna jest typu wątku, a druga klasy string.
- Linia kodu 15-18: nadpisujemy konstruktor GuruThread3, który przyjmuje jeden argument jako typ ciągu (czyli nazwę wątku), który jest przypisywany do zmiennej klasy guruname, a zatem nazwa wątku jest przechowywana.
- Linia kodu 20: Tutaj zastępujemy metodę run () uruchamialnego interfejsu.
- Linia kodu 21: Wyprowadzamy nazwę wątku za pomocą instrukcji println.
- Linia kodu 22-31: Tutaj używamy pętli for z licznikiem zainicjowanym na 0 i nie powinna być mniejsza niż 4 (możemy wziąć dowolną liczbę, stąd pętla będzie działać 4 razy) i zwiększamy licznik. Drukujemy nazwę wątku, a także ustawiamy wątek w stanie uśpienia na 1000 milisekund w bloku try-catch, ponieważ metoda uśpienia wywołała sprawdzony wyjątek.
- Linia kodu 33: Tutaj zastępujemy metodę startu uruchamialnego interfejsu.
- Linia kodu 35: Wyprowadzamy tekst „Rozpoczęto wątek”.
- Linia kodu 36-40: Tutaj bierzemy warunek if, aby sprawdzić, czy zmienna klasy guruthread ma wartość, czy nie. Jeśli ma wartość null, tworzymy instancję za pomocą klasy wątku, która przyjmuje nazwę jako parametr (wartość, do której została przypisana w konstruktorze). Po czym wątek jest uruchamiany metodą start ().
Po wykonaniu powyższego kodu otrzymasz następujące dane wyjściowe:
Dane wyjściowe :
Istnieją więc dwa wątki, dlatego dwa razy otrzymujemy komunikat „Rozpoczęto wątek”.
Otrzymujemy nazwy wątku w postaci, w jakiej je wyprowadziliśmy.
Przechodzi do pętli for, w której drukujemy licznik i nazwę wątku, a licznik zaczyna się od 0.
Pętla jest wykonywana trzykrotnie, a pomiędzy wątkami jest uśpiona przez 1000 milisekund.
Zatem najpierw otrzymujemy guru1, potem guru2, potem znowu guru2, ponieważ nić śpi tutaj przez 1000 milisekund, a potem następny guru1 i znowu guru1, nić śpi przez 1000 milisekund, więc otrzymujemy guru2, a potem guru1.
Podsumowanie :
W tym samouczku omówiliśmy aplikacje wielowątkowe w Javie oraz sposób korzystania z jednego i wielu wątków.
- W przypadku wielowątkowości użytkownicy nie są blokowani, ponieważ wątki są niezależne i mogą wykonywać wiele operacji jednocześnie
- Różne etapy życia nici to:
- Nowy
- Runnable
- Bieganie
- Czekanie
- Nie żyje
- Dowiedzieliśmy się również o synchronizacji między wątkami, dzięki czemu aplikacja działa płynnie.
- Wielowątkowość ułatwia wiele innych zadań aplikacji.