Параллелизм Java - Обзор

Java - это многопоточный язык программирования, что означает, что мы можем разрабатывать многопоточные программы с использованием Java. Многопоточная программа состоит из двух или более частей, которые могут выполняться одновременно, и каждая часть может одновременно обрабатывать разные задачи, оптимально используя доступные ресурсы, особенно когда на вашем компьютере несколько процессоров.

По определению, многозадачность - это когда несколько процессов совместно используют общие ресурсы обработки, такие как ЦП. Многопоточность расширяет идею многозадачности на приложения, где вы можете разделить определенные операции в рамках одного приложения на отдельные потоки. Каждый из потоков может работать параллельно. ОС распределяет время обработки не только между различными приложениями, но и между каждым потоком в приложении.

Многопоточность позволяет писать таким образом, чтобы несколько действий могли выполняться одновременно в одной программе.

Жизненный цикл потока

В своем жизненном цикле поток проходит различные стадии. Например, поток создается, запускается, выполняется и затем умирает. На следующей диаграмме показан полный жизненный цикл потока.

Ниже приведены этапы жизненного цикла -

  • New- Новый поток начинает свой жизненный цикл в новом состоянии. Он остается в этом состоянии, пока программа не запустит поток. Его также называютborn thread.

  • Runnable- После запуска вновь созданного потока он становится работоспособным. Считается, что поток в этом состоянии выполняет свою задачу.

  • Waiting- Иногда поток переходит в состояние ожидания, пока поток ожидает, пока другой поток выполнит задачу. Поток переходит обратно в рабочее состояние только тогда, когда другой поток сигнализирует ожидающему потоку продолжить выполнение.

  • Timed Waiting- Выполняемый поток может войти в состояние ожидания по времени в течение указанного интервала времени. Поток в этом состоянии переходит обратно в рабочее состояние, когда истекает этот временной интервал или когда происходит событие, которого он ожидает.

  • Terminated (Dead) - Выполняемый поток входит в состояние завершения, когда он завершает свою задачу или иным образом завершается.

Приоритеты потоков

Каждый поток Java имеет приоритет, который помогает операционной системе определять порядок, в котором потоки планируются.

Приоритеты потоков Java находятся в диапазоне от MIN_PRIORITY (константа 1) до MAX_PRIORITY (константа 10). По умолчанию каждому потоку дается приоритет NORM_PRIORITY (константа 5).

Потоки с более высоким приоритетом более важны для программы, и им следует выделять процессорное время перед потоками с более низким приоритетом. Однако приоритеты потоков не могут гарантировать порядок, в котором выполняются потоки, и очень сильно зависят от платформы.

Создание потока путем реализации исполняемого интерфейса

Если ваш класс предназначен для выполнения как поток, вы можете добиться этого, реализовав Runnableинтерфейс. Вам нужно будет выполнить три основных шага -

Шаг 1

В качестве первого шага вам необходимо реализовать метод run (), предоставляемый Runnableинтерфейс. Этот метод обеспечивает точку входа для потока, и вы поместите в него свою полную бизнес-логику. Ниже приводится простой синтаксис метода run ():

public void run( )

Шаг 2

На втором этапе вы создадите экземпляр Thread объект, используя следующий конструктор -

Thread(Runnable threadObj, String threadName);

Где threadObj - это экземпляр класса, который реализуетRunnable интерфейс и threadName это имя, данное новому потоку.

Шаг 3

После создания объекта Thread вы можете запустить его, вызвав start()метод, который выполняет вызов метода run (). Ниже приводится простой синтаксис метода start () -

void start();

Example

Вот пример, который создает новый поток и запускает его -

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

Это даст следующий результат -

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.

Создайте поток, расширив класс потока

Второй способ создать поток - создать новый класс, расширяющий Threadclass, используя следующие два простых шага. Этот подход обеспечивает большую гибкость в обработке нескольких потоков, созданных с использованием доступных методов в классе Thread.

Шаг 1

Вам нужно будет переопределить run( )метод, доступный в классе Thread. Этот метод обеспечивает точку входа для потока, и вы поместите в него полную бизнес-логику. Ниже приведен простой синтаксис метода run () -

public void run( )

Шаг 2

После создания объекта Thread вы можете запустить его, вызвав start()метод, который выполняет вызов метода run (). Ниже приводится простой синтаксис метода start () -

void start( );

Example

Вот предыдущая программа, переписанная для расширения потока -

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

Это даст следующий результат -

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.