Wielowątkowość w Java


Każda aplikacja może mieć wiele procesów (instancji). Każdy z tych procesów może być przypisany 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.

Co to jest pojedynczy wątek?

Pojedynczy wątek w Java jest w zasadzie lekką i najmniejszą jednostką przetwarzania. Java używa wątków, używając „Klasy wątków”. Istnieją dwa typy wątków – wątek użytkownika i wątek demona (wątki daemon 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 utworzyć wiele wątków użytkownika i wątków daemon.

Przykład pojedynczego wątku:

package demotest;

public class GuruThread
{
       public static void main(String[] args) {
              System.out.println("Single Thread");
       }
}

Zalety pojedynczego wątku:

  • Zmniejsza obciążenie aplikacji podczas wykonywania pojedynczego wątku w systemie
  • Zmniejsza także koszty utrzymania aplikacji.

Na czym polega wielowątkowość Java?

Wielowątkowość in Java jest procesem 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. Stąd też jest znany jako współbieżność w Java. Każdy wątek przebiega równolegle do siebie. Wiele wątków nie przydziela oddzielnego obszaru pamięci, dlatego oszczędzają pamięć. Ponadto przełączanie kontekstu między wątkami zajmuje mniej czasu.

Przykład wielowątkowy:

package demotest;
public class GuruThread1 implements Runnable
{
       public static void main(String[] args) {
        Thread guruThread1 = new Thread("Guru1");
        Thread guruThread2 = new Thread("Guru2");
        guruThread1.start();
        guruThread2.start();
        System.out.println("Thread names are following:");
        System.out.println(guruThread1.getName());
        System.out.println(guruThread2.getName());
    }
    @Override
    public void run() {
    }
}

Zalety wielowątkowości:

  • Użytkownicy nie są blokowani, ponieważ wątki są niezależne i możemy wykonywać wiele operacji jednocześnie
  • Jako takie wątki są niezależne, inne wątki nie ulegną zmianie, jeśli jeden wątek spełni wyjątek.

Cykl życia wątku w Java

Cykl życia wątku:

Cykl życia wątku w Java
Cykl życia wątku w Java

Istnieją różne etapy cyklu życia nici, jak pokazano na powyższym schemacie:

  1. Nowości
  2. Możliwość uruchomienia
  3. Bieganie
  4. Czekanie
  5. Nie żyje
  1. Nowość: W tej fazie wątek tworzony jest przy pomocy klasy „Thread class”. Pozostaje w tym stanie aż do uruchomienia programu rozpocznie groźba. Nazywa się ją również urodzoną nicią.
  2. Możliwość uruchomienia: Na tej stronie instancja wątku jest wywoływana za pomocą metody start. Kontrola wątku jest przekazywana programowi planującemu w celu zakończenia wykonywania. To zależy od harmonogramu, czy uruchomić wątek.
  3. Bieganie: Kiedy wątek rozpoczyna wykonywanie, stan zostaje zmieniony na stan „uruchomiony”. Harmonogram wybiera jeden wątek z puli wątków i rozpoczyna wykonywanie w aplikacji.
  4. Czekanie: 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. Stąd jeden wątek musi czekać, aż drugi wątek zostanie wykonany. Dlatego ten stan jest nazywany stanem oczekiwania.
  5. Nie żyje: Jest to stan, w którym wątek zostaje zakończony. Wątek jest w stanie uruchomionym, a po zakończeniu przetwarzania znajduje się w „stanie martwym”.


Metody wielowątkowości w Java

Niektóre z powszechnie stosowanych metod dla wątków to:

Metoda wykonania Opis
początek() Ta metoda rozpoczyna wykonywanie wątku i FMV wywołuje metodę run() w wątku.
Uśpienie (w milisekundach) Ta metoda powoduje, że wątek jest uśpiony, więc wykonywanie wątku zostanie wstrzymane na milisekundy, po czym wątek ponownie rozpocznie wykonywanie. Pomaga to w synchronizacji wątków.
getName () Zwraca nazwę wątku.
setPriority(int nowypriorytet) Zmienia priorytet wątku.
dawać () Powoduje zatrzymanie bieżącego wątku i wykonanie innych wątków.

Przykład: W tym programie wielowątkowym w Java Na przykład utworzymy wątek i przyjrzymy się wbudowanym metodom dostępnym dla wątków.

package demotest;
public class thread_example1 implements Runnable {
    @Override
    public void run() {
    }
    public static void main(String[] args) {
        Thread guruthread1 = new Thread();
        guruthread1.start();
        try {
            guruthread1.sleep(1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        guruthread1.setPriority(1);
        int gurupriority = guruthread1.getPriority();
        System.out.println(gurupriority);
        System.out.println("Thread Running");
  }
}

Wyjaśnienie kodu:

  • Linia kodu 2: Tworzymy klasę „thread_Example1”, która implementuje interfejs Runnable (powinien być zaimplementowany przez dowolną klasę, której instancje mają być wykonywane przez wątek).
  • Linia kodu 4: Zastępuje metodę uruchamiania uruchamialnego interfejsu, ponieważ zastąpienie tej metody jest obowiązkowe
  • 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”, tworząc instancję nowej klasy wątku.
  • Linia kodu 8: użyjemy metody „start” wątku przy użyciu instancji „guruthread1”. Tutaj wątek rozpocznie wykonywanie.
  • Linia kodu 10: Tutaj używamy metody „uśpienia” wątku przy użyciu instancji „guruthread1”. W związku z tym wątek będzie uśpiony przez 1000 milisekund.
  • Kod 9-14: Tutaj umieściliśmy metodę uśpienia w bloku try catch, ponieważ wystąpił sprawdzany wyjątek, tj. 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ą getPriority()
  • Linia kodu 17: Tutaj drukujemy 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ący wynik:

Przykład wątku w Java

Wyjście:

5 to priorytet wątku, a działanie wątku to tekst będący wynikiem naszego kodu.

Java Wątek Synchronizacja

W wielowątkowości występuje asynchroniczne zachowanie programów. Jeśli jeden wątek zapisuje pewne dane, a inny wątek odczytuje dane w tym samym czasie, może to powodować niespójność w aplikacji. Gdy istnieje potrzeba dostępu do współdzielonych zasobów przez dwa lub więcej wątków, wówczas wykorzystywane jest podejście synchronizacji. Java zapewnił 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ż wątek zakończy zsynchronizowany blok i wyjdzie z niego. W ten sposób synchronizacja pomaga w wielowątkowej aplikacji. Jeden wątek musi czekać, aż inny wątek zakończy swoje wykonywanie, tylko wtedy inne wątki mogą zostać wykonane.

Można to zapisać w następującej formie:

Synchronized(object)
{  
        //Block of statements to be synchronized
}

Wielowątkowość w Java Przykładowe programy

W tej wielowątkowości Java na przykład weźmiemy dwa wątki i pobierzemy nazwy wątków.

Przykład1:

GuruThread1.java
package demotest;
public class GuruThread1 implements Runnable{

    /**
     * @param args
     */
    public static void main(String[] args) {
        Thread guruThread1 = new Thread("Guru1");
        Thread guruThread2 = new Thread("Guru2");
        guruThread1.start();
        guruThread2.start();
        System.out.println("Thread names are following:");
        System.out.println(guruThread1.getName());
        System.out.println(guruThread2.getName());
    }
    @Override
    public void run() {
    }
}

Wyjaśnienie kodu:

  • Linia kodu 3: Przyjęliśmy klasę „GuruThread1”, która implementuje Runnable (powinna być zaimplementowana przez dowolną klasę, której instancje mają być wykonywane przez wątek).
  • Linia kodu 8: Jest to główna metoda tej klasy
  • Linia kodu 9: Tutaj tworzymy instancję klasy Thread i tworzymy instancję o nazwie „guruThread1” oraz tworzymy wątek.
  • Linia kodu 10: Tutaj tworzymy instancję klasy Thread i tworzymy instancję o nazwie „guruThread2” oraz tworzymy wątek.
  • Linia kodu 11: Rozpoczynamy wątek czyli guruThread1.
  • Linia kodu 12: Rozpoczynamy wątek czyli guruThread2.
  • Linia kodu 13: Wyświetlanie tekstu w postaci „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 za pomocą metody getName() klasy wątku.

Po wykonaniu powyższego kodu otrzymasz następujący wynik:

Java Przykład wielowątkowości

Wyjście:

Nazwy wątków są tutaj wyświetlane jako

  • Guru1
  • Guru2

2 przykład:

W tej wielowątkowości w Java na przykład dowiemy się, jak przesłaniać metody run() i start() uruchamialnego interfejsu, a następnie utworzymy dwa wątki tej klasy i odpowiednio je uruchomimy.

Poza tym chodzimy na dwie lekcje,

  • Taki, który zaimplementuje uruchamialny interfejs i
  • Kolejny, który będzie miał metodę główną i zostanie odpowiednio wykonany.
package demotest;
public class GuruThread2 {
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  GuruThread3 threadguru1 = new GuruThread3("guru1");
  threadguru1.start();
  GuruThread3 threadguru2 = new GuruThread3("guru2");
  threadguru2.start();
 }
}
class GuruThread3 implements Runnable {
 Thread guruthread;
 private String guruname;
 GuruThread3(String name) {
  guruname = name;
 }
 @Override
 public void run() {
  System.out.println("Thread running" + guruname);
  for (int i = 0; i < 4; i++) {
   System.out.println(i);
   System.out.println(guruname);
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    System.out.println("Thread has been interrupted");
   }
  }
 }
 public void start() {
  System.out.println("Thread started");
  if (guruthread == null) {
   guruthread = new Thread(this, guruname);
   guruthread.start();
  }
 }
}

Wyjaśnienie kodu:

  • Linia kodu 2: Tutaj bierzemy klasę „GuruThread2”, która będzie zawierała główną metodę.
  • Linia kodu 4: Tutaj bierzemy główną metodę klasy.
  • Linia kodu 6-7: Tutaj tworzymy instancję klasy GuruThread3 (która jest utworzona w poniższych linijkach kodu) jako „threadguru1” i rozpoczynamy wątek.
  • Linia kodu 8-9: Tutaj tworzymy kolejną instancję klasy GuruThread3 (która jest utworzona w poniższych linijkach kodu) jako „threadguru2” i rozpoczynamy wątek.
  • Linia kodu 11: Tutaj tworzymy klasę „GuruThread3”, która implementuje uruchamialny interfejs (powinien być zaimplementowany przez dowolną klasę, której instancje mają być wykonywane przez wątek).
  • Linia kodu 13-14: bierzemy dwie zmienne klasowe, z których jedna jest typu thread class, a druga klasy string.
  • Linia kodu 15-18: nadpisujemy konstruktor GuruThread3, który przyjmuje jeden argument jako typ ciągu znaków (który jest nazwą wątku), który jest przypisywany do zmiennej klasowej guruname i stąd przechowywana jest nazwa wątku.
  • Linia kodu 20: Tutaj nadpisujemy metodę run() uruchamialnego interfejsu.
  • Linia kodu 21: Wypisujemy nazwę wątku za pomocą instrukcji println.
  • Linia kodu 22-31: Tutaj używamy pętli for z licznikiem zainicjowanym na 0, który nie powinien być mniejszy niż 4 (możemy przyjąć dowolną liczbę, dlatego tutaj pętla wykona się 4 razy) i zwiększamy licznik. Drukujemy nazwę wątku, a także powodujemy, że wątek jest uśpiony na 1000 milisekund w bloku try-catch, gdy metoda uśpienia zgłosiła sprawdzony wyjątek.
  • Linia kodu 33: Tutaj nadpisujemy metodę start uruchamialnego interfejsu.
  • Linia kodu 35: Wypisujemy tekst „Wątek rozpoczęty”.
  • Linia kodu 36-40: Tutaj bierzemy warunek if, aby sprawdzić, czy zmienna klasy guruthread ma wartość, czy nie. Jeśli ma wartość null, to tworzymy instancję za pomocą klasy wątku, która przyjmuje nazwę jako parametr (wartość, dla której przypisano w konstruktorze). Po czym wątek jest uruchamiany metodą start().

Po wykonaniu powyższego kodu otrzymasz następujący wynik:

Przykład wielowątkowości w Java

Wydajność:

Istnieją dwa wątki, dlatego dwukrotnie otrzymujemy komunikat „Wątek rozpoczęty”.

Otrzymujemy nazwy wątków w postaci, w jakiej je wygenerowaliśmy.

Przechodzi do pętli for, w której drukujemy licznik i nazwę wątku, a licznik zaczyna się od 0.

Pętla wykonuje się trzy razy, a pomiędzy nimi wątek jest uśpiony na 1000 milisekund.

Zatem najpierw otrzymujemy guru1, potem guru2, potem znowu guru2, ponieważ wątek śpi tutaj przez 1000 milisekund, a następnie następnego guru1 i znowu guru1, wątek śpi przez 1000 milisekund, więc otrzymujemy guru2, a następnie guru1.

Podsumowanie

W tym samouczku widzieliśmy aplikacje wielowątkowe w Java i jak używać jednego i wielu wątków w Java.

  • Wyjaśnij wielowątkowość w Java: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 cyklu życia nici to:
    • Nowości
    • Możliwość uruchomienia
    • Bieganie
    • Czekanie
    • Nie żyje
  • Dowiedzieliśmy się także o synchronizacja pomiędzy wątkami, co pomaga w płynnym działaniu aplikacji.
  • Programowanie wielowątkowe w Java ułatwia wiele innych zadań aplikacyjnych.