Java - wielowątkowość

Java to wielowątkowy język programowania, co oznacza, że ​​możemy tworzyć wielowątkowe programy przy użyciu języka Java. Program wielowątkowy zawiera dwie lub więcej części, które mogą działać jednocześnie, a każda część może jednocześnie obsługiwać inne zadanie, optymalnie wykorzystując dostępne zasoby, szczególnie gdy komputer ma wiele procesorów.

Z definicji wielozadaniowość to sytuacja, w której wiele procesów korzysta ze wspólnych zasobów przetwarzania, takich jak procesor. Wielowątkowość rozszerza ideę wielozadaniowości na aplikacje, w których można podzielić określone operacje w ramach jednej aplikacji na osobne wątki. Każdy z wątków może działać równolegle. System operacyjny dzieli czas przetwarzania nie tylko na różne aplikacje, ale także na każdy wątek w aplikacji.

Wielowątkowość umożliwia pisanie w taki sposób, że wiele działań może być wykonywanych jednocześnie w tym samym programie.

Cykl życia wątku

Wątek przechodzi przez różne etapy swojego cyklu życia. Na przykład wątek rodzi się, uruchamia, działa, a następnie umiera. Poniższy diagram przedstawia pełny cykl życia wątku.

Oto etapy cyklu życia -

  • New- Nowy wątek rozpoczyna swój cykl życia w nowym stanie. Pozostaje w tym stanie, dopóki program nie uruchomi wątku. Jest również określany jako plikborn thread.

  • Runnable- Po uruchomieniu nowo powstałego wątku wątek staje się gotowy do uruchomienia. Uważa się, że wątek w tym stanie wykonuje swoje zadanie.

  • Waiting- Czasami wątek przechodzi w stan oczekiwania, podczas gdy wątek oczekuje na wykonanie zadania przez inny wątek. Wątek przechodzi z powrotem do stanu, który można uruchomić, tylko wtedy, gdy inny wątek sygnalizuje oczekującemu wątkowi, aby kontynuował wykonywanie.

  • Timed Waiting- Działający wątek może wejść w czasowy stan oczekiwania przez określony przedział czasu. Wątek w tym stanie przechodzi z powrotem do stanu, który można uruchomić, po wygaśnięciu tego przedziału czasu lub wystąpieniu zdarzenia, na które oczekuje.

  • Terminated (Dead) - Wątek, który można uruchomić, przechodzi w stan przerwania po zakończeniu zadania lub w inny sposób.

Priorytety wątków

Każdy wątek Java ma priorytet, który pomaga systemowi operacyjnemu określić kolejność planowania wątków.

Priorytety wątków Java znajdują się w zakresie od MIN_PRIORITY (stała 1) do MAX_PRIORITY (stała 10). Domyślnie każdy wątek ma priorytet NORM_PRIORITY (stała 5).

Wątki o wyższym priorytecie są ważniejsze dla programu i powinny mieć przydzielony czas procesora przed wątkami o niższym priorytecie. Jednak priorytety wątków nie mogą zagwarantować kolejności wykonywania wątków i są bardzo zależne od platformy.

Utwórz wątek, implementując uruchamialny interfejs

Jeśli twoja klasa ma być wykonywana jako wątek, możesz to osiągnąć, implementując plik Runnableberło. Będziesz musiał wykonać trzy podstawowe kroki -

Krok 1

Pierwszym krokiem jest zaimplementowanie metody run () udostępnianej przez plik Runnableberło. Ta metoda zapewnia punkt wejścia dla wątku, a całą logikę biznesową umieścisz w tej metodzie. Poniżej znajduje się prosta składnia metody run () -

public void run( )

Krok 2

W drugim kroku utworzysz instancję Thread obiekt przy użyciu następującego konstruktora -

Thread(Runnable threadObj, String threadName);

Gdzie threadObj jest wystąpieniem klasy, która implementujeRunnable interfejs i threadName to nazwa nadana nowemu wątkowi.

Krok 3

Po utworzeniu obiektu Thread możesz go uruchomić, wywołując start()metoda, która wykonuje wywołanie metody run (). Poniżej znajduje się prosta składnia metody start () -

void start();

Przykład

Oto przykład, który tworzy nowy wątek i uruchamia go -

class RunnableDemo implements Runnable {
   private Thread t;
   private String threadName;
   
   RunnableDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      RunnableDemo R1 = new RunnableDemo( "Thread-1");
      R1.start();
      
      RunnableDemo R2 = new RunnableDemo( "Thread-2");
      R2.start();
   }   
}

To da następujący wynik -

Wynik

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Utwórz wątek, rozszerzając klasę wątku

Drugim sposobem na utworzenie wątku jest utworzenie nowej klasy, która rozszerza Threadw dwóch prostych krokach. Takie podejście zapewnia większą elastyczność w obsłudze wielu wątków utworzonych przy użyciu metod dostępnych w klasie Thread.

Krok 1

Będziesz musiał zmienić run( )metoda dostępna w klasie Thread. Ta metoda zapewnia punkt wejścia dla wątku, a całą logikę biznesową umieścisz w tej metodzie. Poniżej znajduje się prosta składnia metody run () -

public void run( )

Krok 2

Po utworzeniu obiektu Thread możesz go uruchomić, wywołując start()metoda, która wykonuje wywołanie metody run (). Poniżej znajduje się prosta składnia metody start () -

void start( );

Przykład

Oto poprzedni program przepisany w celu rozszerzenia wątku -

class ThreadDemo extends Thread {
   private Thread t;
   private String threadName;
   
   ThreadDemo( String name) {
      threadName = name;
      System.out.println("Creating " +  threadName );
   }
   
   public void run() {
      System.out.println("Running " +  threadName );
      try {
         for(int i = 4; i > 0; i--) {
            System.out.println("Thread: " + threadName + ", " + i);
            // Let the thread sleep for a while.
            Thread.sleep(50);
         }
      } catch (InterruptedException e) {
         System.out.println("Thread " +  threadName + " interrupted.");
      }
      System.out.println("Thread " +  threadName + " exiting.");
   }
   
   public void start () {
      System.out.println("Starting " +  threadName );
      if (t == null) {
         t = new Thread (this, threadName);
         t.start ();
      }
   }
}

public class TestThread {

   public static void main(String args[]) {
      ThreadDemo T1 = new ThreadDemo( "Thread-1");
      T1.start();
      
      ThreadDemo T2 = new ThreadDemo( "Thread-2");
      T2.start();
   }   
}

To da następujący wynik -

Wynik

Creating Thread-1
Starting Thread-1
Creating Thread-2
Starting Thread-2
Running Thread-1
Thread: Thread-1, 4
Running Thread-2
Thread: Thread-2, 4
Thread: Thread-1, 3
Thread: Thread-2, 3
Thread: Thread-1, 2
Thread: Thread-2, 2
Thread: Thread-1, 1
Thread: Thread-2, 1
Thread Thread-1 exiting.
Thread Thread-2 exiting.

Metody wątku

Poniżej znajduje się lista ważnych metod dostępnych w klasie Thread.

Sr.No. Metoda i opis
1

public void start()

Uruchamia wątek w oddzielnej ścieżce wykonywania, a następnie wywołuje metodę run () w tym obiekcie Thread.

2

public void run()

Jeśli wystąpienie tego obiektu Thread zostało utworzone przy użyciu oddzielnego elementu docelowego Runnable, metoda run () jest wywoływana na tym obiekcie Runnable.

3

public final void setName(String name)

Zmienia nazwę obiektu Thread. Istnieje również metoda getName () do pobierania nazwy.

4

public final void setPriority(int priority)

Ustawia priorytet tego obiektu Thread. Możliwe wartości mieszczą się w przedziale od 1 do 10.

5

public final void setDaemon(boolean on)

Parametr true oznacza ten wątek jako wątek demona.

6

public final void join(long millisec)

Bieżący wątek wywołuje tę metodę w drugim wątku, powodując blokowanie bieżącego wątku do momentu zakończenia drugiego wątku lub przejścia określonej liczby milisekund.

7

public void interrupt()

Przerywa ten wątek, powodując kontynuację wykonywania, jeśli został zablokowany z jakiegokolwiek powodu.

8

public final boolean isAlive()

Zwraca wartość true, jeśli wątek żyje, czyli w dowolnym momencie po uruchomieniu wątku, ale przed zakończeniem.

Poprzednie metody są wywoływane na określonym obiekcie Thread. Następujące metody w klasie Thread są statyczne. Wywołanie jednej z metod statycznych wykonuje operację na aktualnie uruchomionym wątku.

Sr.No. Metoda i opis
1

public static void yield()

Powoduje, że aktualnie działający wątek podlega innym wątkom o tym samym priorytecie, które czekają na zaplanowanie.

2

public static void sleep(long millisec)

Powoduje, że aktualnie działający wątek blokuje się na co najmniej określoną liczbę milisekund.

3

public static boolean holdsLock(Object x)

Zwraca wartość true, jeśli bieżący wątek utrzymuje blokadę na danym Object.

4

public static Thread currentThread()

Zwraca odwołanie do aktualnie uruchomionego wątku, który jest wątkiem, który wywołuje tę metodę.

5

public static void dumpStack()

Wyświetla ślad stosu dla aktualnie uruchomionego wątku, co jest przydatne podczas debugowania aplikacji wielowątkowej.

Przykład

Poniższy program ThreadClassDemo przedstawia niektóre z tych metod klasy Thread. Rozważ klasęDisplayMessage które implementuje Runnable -

// File Name : DisplayMessage.java
// Create a thread to implement Runnable

public class DisplayMessage implements Runnable {
   private String message;
   
   public DisplayMessage(String message) {
      this.message = message;
   }
   
   public void run() {
      while(true) {
         System.out.println(message);
      }
   }
}

Poniżej znajduje się kolejna klasa, która rozszerza klasę Thread -

// File Name : GuessANumber.java
// Create a thread to extentd Thread

public class GuessANumber extends Thread {
   private int number;
   public GuessANumber(int number) {
      this.number = number;
   }
   
   public void run() {
      int counter = 0;
      int guess = 0;
      do {
         guess = (int) (Math.random() * 100 + 1);
         System.out.println(this.getName() + " guesses " + guess);
         counter++;
      } while(guess != number);
      System.out.println("** Correct!" + this.getName() + "in" + counter + "guesses.**");
   }
}

Poniżej znajduje się główny program, który korzysta z wyżej zdefiniowanych klas -

// File Name : ThreadClassDemo.java
public class ThreadClassDemo {

   public static void main(String [] args) {
      Runnable hello = new DisplayMessage("Hello");
      Thread thread1 = new Thread(hello);
      thread1.setDaemon(true);
      thread1.setName("hello");
      System.out.println("Starting hello thread...");
      thread1.start();
      
      Runnable bye = new DisplayMessage("Goodbye");
      Thread thread2 = new Thread(bye);
      thread2.setPriority(Thread.MIN_PRIORITY);
      thread2.setDaemon(true);
      System.out.println("Starting goodbye thread...");
      thread2.start();

      System.out.println("Starting thread3...");
      Thread thread3 = new GuessANumber(27);
      thread3.start();
      try {
         thread3.join();
      } catch (InterruptedException e) {
         System.out.println("Thread interrupted.");
      }
      System.out.println("Starting thread4...");
      Thread thread4 = new GuessANumber(75);
      
      thread4.start();
      System.out.println("main() is ending...");
   }
}

Spowoduje to następujący wynik. Możesz próbować tego przykładu wielokrotnie, a za każdym razem otrzymasz inny wynik.

Wynik

Starting hello thread...
Starting goodbye thread...
Hello
Hello
Hello
Hello
Hello
Hello
Goodbye
Goodbye
Goodbye
Goodbye
Goodbye
.......

Główne koncepcje wielowątkowości w języku Java

Robiąc programowanie wielowątkowe w Javie, musisz mieć bardzo przydatne następujące pojęcia -

  • Co to jest synchronizacja wątków?

  • Obsługa komunikacji między wątkami

  • Obsługa zakleszczenia wątków

  • Główne operacje wątkowe