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

В java, когда мы запускаем основной метод, JVM по умолчанию в фоновом режиме создает для вас основной поток, в котором он выполняет программу. Это называется основным потоком.

Что такое поток?

Это не что иное, как путь выполнения программы. Точно так же, как мы читаем эту статью построчно, JVM также считывает и выполняет ваш код построчно в методе main.

В чем разница между процессом и потоком?

Исполняемая программа — это не что иное, как процесс. В процессе может быть запущено несколько потоков. Процесс также можно назвать потоком. Но поток нельзя назвать процессом.Запутанно?? ОК. Давайте посмотрим пример. Открытие браузера Chrome — это не что иное, как процесс. Вы начали поиск URL-адреса на вкладке, и он выполняется в потоке. Если вы откроете другую вкладку и одновременно начнете искать другую тему, то будет создана еще одна ветка. Таким образом, в одном процессе у нас может быть несколько потоков.

Как создать тему?

Вы можете создать класс как класс Thread, расширив класс Thread или внедрив Runnable Interface.

public class MyThread extends Thread {

    public void run(){
       System.out.println("MyThread running");
    }
  }

Теперь мы можем вызвать MyThread и запустить поток следующим образом:

MyThread myThread = new MyThread();
  myThread.start();

Вызов start() вернется, как только поток будет запущен. Он не будет ждать, пока метод run() не будет выполнен. Метод run() будет выполняться так, как если бы он выполнялся другим процессором. Когда метод run() выполняется, он выводит текст «MyThread running».

Мы также можем создать анонимный класс потока следующим образом:

Thread thread = new Thread(){
    public void run(){
      System.out.println("Thread Running");
    }
  }

  thread.start();

Давайте посмотрим, как мы можем реализовать ту же функциональность, используя интерфейс Runnable.

public class MyRunnable implements Runnable {

    public void run(){
       System.out.println("MyRunnable running");
    }
  }

Если мы переключимся на анонимный класс для того же

Runnable myRunnable =
    new Runnable(){
        public void run(){
            System.out.println("Runnable running");
        }
    }

Используя лямбда-выражения, это еще больше упрощается.

Runnable runnable =
        () -> { System.out.println("Lambda Runnable running"); };

Чтобы запустить поток интерфейса runnable, мы должны передать экземпляр Runnable классу Thread.

Thread thread = new Thread(runnable);
thread.start()

Что такое этапы цепочки?

У нас есть 5 различных этапов жизненного цикла потока. Когда мы создали поток, состояние было новым. Теперь я назвал свой метод start(), он начнет вызывать run(). Во время этого процесса поток находится в состоянии Runnable. После вызова метода run() он запускает задачи в потоке. Это переводит его в рабочее состояние.

Пока он работает, я могу попросить поток немного отдохнуть. Для этого я использую Thread.sleep(). Поток переходит в заблокированное состояние на указанное количество времени, а затем возвращается в рабочее состояние, а затем выполняется.

какое значение функции join()

Предположим, у меня есть программа, в которой я запускаю 2 потока, скажем, T1 и T2. В конце я печатаю заявление «выполнение завершено». Каков мой результат?

Только представьте, T1 выполняет задачу, а T2 занят выполнением другой задачи. Пока эти 2 потока заняты, основной поток свободен и видит, что в конце есть оператор. Таким образом, он выполняет оператор и печатает завершение выполнения еще до завершения потоков.

Это нежелательно, верно? поэтому, чтобы преодолеть это, у нас есть метод с именем join().

Мы можем вызвать t1.join() непосредственно перед оператором. Теперь основной поток ожидает завершения потока t1, а затем начинает выполнение инструкции.

Что делать, если мне нужно обновить одну и ту же переменную из двух потоков одновременно?

Ммм… Я узнал, что такое нить и как она работает. У меня возникло сомнение. Предположим, у меня есть переменная say count, и она нужна для двух задач, которые выполняются двумя разными потоками. Мой поток t1 увеличивает счетчик в цикле 5 раз, и если t2 также обновляет счетчик одновременно в цикле, то при чтении значения t2 он может все еще не иметь последнее значение, обновленное t1. поэтому мы можем получить конфликтующий вывод.

У вас есть решение для этого?

Ява нашел решение. Да, они ввели ключевое слово под названием синхронизированный. Он блокирует метод до тех пор, пока поток не освободит его, поэтому только один поток может воздействовать на него в один момент времени. Другой поток ждет, пока он освободится, а затем вступает в свою очередь. Таким образом, у нас нет конфликтных выходных данных, и каждый раз, когда мы обращаемся к count, мы получаем последнее значение.

Прохладный. Теперь у меня другая проблема. У меня есть 2 переменные x и y, которые обновляются двумя разными методами update_x() и update_y(). Они оба помечены как синхронизированные, как было предложено. В t1 мой первый оператор — update_x(), поэтому я заблокировал метод и начал обновление. Тем временем поток t2 выполняет оператор update_y(). Внутри моего update_x() я вызываю update_y(), так что теперь поток t1 ожидает, пока t2 выпустит метод update_y().

t2 выполняет метод update_y(), внутренне вызывает update_x() и начинает ждать освобождения t1. Это переходит в бесконечный цикл.

Такая ситуация называется тупиковой.

Как выйти из тупиковой ситуации?

  • Используйте блокировку только для тех объектов/методов, которые необходимы. Избегайте использования для нескольких объектов
  • Предоставьте тайм-аут, если поток t1 не смог получить блокировку даже после ожидания определенного времени, мы можем сгенерировать исключение с соответствующим сообщением и выйти из ожидания метода.
  • Избегайте использования функции join(), которая ожидает завершения другого потока в течение неопределенного периода времени.
  • Избегайте вложенных блокировок. Если мы уже предоставили блокировку одному потоку, избегайте предоставления блокировки другим потокам.

На данный момент мы хорошо знакомы с основами многопоточности. Давайте углубимся в нашу следующую статью. Продолжай читать..!! Оставаться в курсе..!!