Współbieżność Java - omówienie

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 gdy wystąpi zdarzenie, 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 są 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();

Example

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 -

Output

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( );

Example

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 -

Output

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.