Многопоточность в Java


Любое приложение может иметь несколько процессов (экземпляров). Каждый из этих процессов может быть назначен либо как один поток, либо как несколько потоков. В этом руководстве мы увидим, как выполнять несколько задач одновременно, а также узнаем больше о потоках и синхронизации между потоками.

Что такое одиночная нить?

Одна нить в Java По сути, это легкая и наименьшая единица обработки. Java использует потоки с помощью «Thread Class». Существует два типа потоков – пользовательский поток и поток демона (демонические потоки используются, когда мы хотим очистить приложение и используются в фоновом режиме). Когда приложение запускается впервые, создается пользовательский поток. После этого мы можем создать много пользовательских потоков и демонических потоков.

Пример одного потока:

package demotest;

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

Преимущества однопоточного:

  • Снижает накладные расходы в приложении, поскольку в системе выполняется один поток.
  • Кроме того, это снижает затраты на обслуживание приложения.

Что такое многопоточность Java?

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

Пример многопоточности:

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

Преимущества многопоточности:

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

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

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

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

Существуют различные этапы жизненного цикла потока, как показано на диаграмме выше:

  1. Новое
  2. Работоспособен
  3. Бег
  4. Ожидание
  5. мертв
  1. Новое: На этом этапе поток создается с использованием класса «Класс потока». Он остается в этом состоянии до тех пор, пока программа не начинается нить. Ее еще называют рожденной нитью.
  2. Работоспособен: На этой странице экземпляр потока вызывается с помощью метода start. Управление потоком передается планировщику для завершения выполнения. От планировщика зависит, запускать ли поток.
  3. Бег: Когда поток начинает выполняться, его состояние меняется на «работающее». Планировщик выбирает один поток из пула потоков и начинает его выполнение в приложении.
  4. Ожидание: Это состояние, когда потоку приходится ждать. Поскольку в приложении выполняется несколько потоков, существует необходимость синхронизации между потоками. Следовательно, один поток должен ждать, пока другой поток не выполнится. Поэтому это состояние называется состоянием ожидания.
  5. Мертвая: Это состояние, когда поток завершается. Поток находится в рабочем состоянии, и как только он завершил обработку, он находится в «мертвом состоянии».


Методы многопоточности в Java

Некоторые из часто используемых методов для потоков:

Способ доставки Описание
Начало() Этот метод запускает выполнение потока и JVM вызывает метод run() в потоке.
Сон (целое число миллисекунд) Этот метод переводит поток в спящий режим, поэтому выполнение потока приостанавливается на предоставленные миллисекунды, и после этого поток снова начинает выполнение. Это помогает в синхронизации потоков.
GetName () Он возвращает имя потока.
setPriority (интервал новый приоритет) Это меняет приоритет потока.
урожай () Это приводит к остановке текущего потока и выполнению других потоков.

Пример: В этой многопоточной программе в Java Например, мы собираемся создать поток и изучить встроенные методы, доступные для потоков.

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

Расшифровка кода:

  • Строка кода 2: Мы создаем класс «thread_Example1», который реализует интерфейс Runnable (он должен быть реализован любым классом, экземпляры которого предназначены для выполнения потоком).
  • Строка кода 4: Он переопределяет метод запуска работающего интерфейса, поскольку переопределение этого метода является обязательным.
  • Строка кода 6: Здесь мы определили основной метод, в котором начнем выполнение потока.
  • Строка кода 7: Здесь мы создаем новое имя потока как «guruthread1», создавая экземпляр нового класса потока.
  • Строка кода 8: мы будем использовать метод «start» потока, используя экземпляр «guruthread1». Здесь поток начнет выполняться.
  • Строка кода 10: Здесь мы используем метод «sleep» потока, использующего экземпляр «guruthread1». Следовательно, поток будет спать в течение 1000 миллисекунд.
  • Код 9-14: Здесь мы поместили метод сна в блок try catch, поскольку возникает проверенное исключение, т. е. прерванное исключение.
  • Строка кода 15: Здесь мы устанавливаем приоритет потока на 1, независимо от того, какой приоритет у него был.
  • Строка кода 16: Здесь мы получаем приоритет потока с помощью getPriority().
  • Строка кода 17: Здесь мы печатаем значение, полученное из getPriority.
  • Строка кода 18: Здесь мы пишем текст о том, что поток запущен.

При выполнении приведенного выше кода вы получите следующий результат:

Пример темы в Java

Вывод:

5 — это приоритет потока, а Thread Running — это текст, который является результатом нашего кода.

Java Нить Syncхронизация

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

При таком подходе, как только поток достигает синхронизированного блока, никакой другой поток не может вызвать этот метод для того же объекта. Все потоки должны ждать, пока этот поток не завершит синхронизированный блок и не выйдет из него. Таким образом, синхронизация помогает в многопоточном приложении. Один поток должен ждать, пока другой поток не завершит свое выполнение, и только после этого другим потокам разрешается выполняться.

Его можно записать в следующем виде:

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

Многопоточность в Java Примеры программ

В этой многопоточности Java Например, мы возьмем два потока и получим имена потоков.

Example1:

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

Расшифровка кода:

  • Строка кода 3: Мы взяли класс GuruThread1, который реализует Runnable (он должен быть реализован любым классом, экземпляры которого предназначены для выполнения потоком).
  • Строка кода 8: Это основной метод класса
  • Строка кода 9: Здесь мы создаем экземпляр класса Thread, создаем экземпляр с именем «guruThread1» и создаем поток.
  • Строка кода 10: Здесь мы создаем экземпляр класса Thread, создаем экземпляр с именем «guruThread2» и создаем поток.
  • Строка кода 11: Мы запускаем поток, т.е. guruThread1.
  • Строка кода 12: Мы запускаем поток, т.е. guruThread2.
  • Строка кода 13: Вывод текста в виде «Имена потоков следующие:»
  • Строка кода 14: Получение имени потока 1 с помощью метода getName() класса потока.
  • Строка кода 15: Получение имени потока 2 с помощью метода getName() класса потока.

При выполнении приведенного выше кода вы получите следующий результат:

Java Пример многопоточности

Вывод:

Имена потоков выводятся здесь как

  • Guru1
  • Guru2

Пример 2:

В этой многопоточности в Java Например, мы узнаем о переопределении методов run() и start() работоспособного интерфейса, создадим два потока этого класса и запустим их соответственно.

Кроме того, мы учимся на двух занятиях,

  • Тот, который будет реализовывать работоспособный интерфейс и
  • Еще один, который будет иметь основной метод и выполняться соответствующим образом.
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();
  }
 }
}

Расшифровка кода:

  • Строка кода 2: Здесь мы берем класс GuruThread2, в котором будет основной метод.
  • Строка кода 4: Здесь мы берем основной метод класса.
  • Строка кода 6–7: Здесь мы создаем экземпляр класса GuruThread3 (который создается в строках кода ниже) как «threadguru1» и запускаем поток.
  • Строка кода 8–9: Здесь мы создаем еще один экземпляр класса GuruThread3 (который создается в строках кода ниже) как «threadguru2» и запускаем поток.
  • Строка кода 11: Здесь мы создаем класс «GuruThread3», который реализует работоспособный интерфейс (он должен быть реализован любым классом, экземпляры которого предназначены для выполнения потоком).
  • Строка кода 13–14: мы берем две переменные класса, одна из которых относится к классу потока, а другая — к классу строк.
  • Строка кода 15–18: мы переопределяем конструктор GuruThread3, который принимает один аргумент в качестве строкового типа (имя потока), который присваивается переменной класса guruname и, следовательно, сохраняется имя потока.
  • Строка кода 20: Здесь мы переопределяем метод run() работающего интерфейса.
  • Строка кода 21: Мы выводим имя потока с помощью оператора println.
  • Строка кода 22–31: Здесь мы используем цикл for со счетчиком, инициализированным равным 0, и он не должен быть меньше 4 (мы можем взять любое число, поэтому здесь цикл будет выполняться 4 раза) и увеличиваем счетчик. Мы печатаем имя потока, а также переводим его в спящий режим на 1000 миллисекунд в блоке try-catch, поскольку метод сна вызывает проверенное исключение.
  • Строка кода 33: Здесь мы переопределяем метод запуска работающего интерфейса.
  • Строка кода 35: Выводим текст «Тема запущена».
  • Строка кода 36–40: Здесь мы используем условие if, чтобы проверить, имеет ли переменная класса guruthread значение или нет. Если его значение равно нулю, мы создаем экземпляр, используя класс потока, который принимает имя в качестве параметра (значение для которого было присвоено в конструкторе). После чего поток запускается с помощью метода start().

Когда вы выполните приведенный выше код, вы получите следующий результат:

Пример многопоточности в Java

Результат:

Есть два потока, следовательно, мы дважды получаем сообщение «Поток запущен».

Мы получаем имена потоков в том виде, в каком мы их вывели.

Он входит в цикл for, где мы печатаем счетчик и имя потока, а счетчик начинается с 0.

Цикл выполняется три раза, а между ними поток приостанавливается на 1000 миллисекунд.

Следовательно, сначала мы получаем guru1, затем guru2, затем снова guru2, потому что поток спит здесь на 1000 миллисекунд, а затем следующий guru1 и снова guru1, поток спит 1000 миллисекунд, поэтому мы получаем guru2, а затем guru1.

Резюме

В этом уроке мы увидели многопоточные приложения в Java и как использовать однопоточный и многопоточный процесс в Java.

  • Объясните многопоточность в Java: в многопоточности пользователи не блокируются, поскольку потоки независимы и могут выполнять несколько операций одновременно
  • Различные этапы жизненного цикла потока:
    • Новое
    • Работоспособен
    • Бег
    • Ожидание
    • мертв
  • Мы также узнали о синхронизация между потоками, что помогает приложению работать бесперебойно.
  • Многопоточное программирование в Java упрощает многие другие прикладные задачи.