Многопоточность в Java – руководство с примерами

В этом руководстве мы рассмотрим, как выполняется многопоточность 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() {
    }

}

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

  • При выполнении одного потока снижается нагрузка на приложение;
  • Уменьшается стоимость обслуживания приложения.

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

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

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

Все потоки выполняются параллельно друг другу. Для каждого отдельного потока не выделяется память, что приводит к ее экономии. Кроме этого переключение между потоками занимает меньше времени.

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

package demotest;
public class GuruMultithread implements Runnable{

    /**
     * @param args
     */
    public static void main(String[] args) {
             Thread guruthread1 = new Thread();
             guruThread1.start();
             Thread guruthread2 = new Thread();
             guruThread2.start();
    }

    @Override
    public void run() {
        // TODO Автоматически сгенерированный метод stub
        
    }

}

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

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

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

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

Multithread1

Стадии жизни потока:

  1. Новый;
  2. Готовый к выполнению;
  3. Выполняемый;
  4. Ожидающий;
  5. Остановленный.
  1. Новый: в этой фазе поток создается с помощью класса Thread. Он остается в этом состоянии, пока программа его не запустит;
  2. Готовый к выполнению: экземпляр потока вызывается с помощью метода Start. Управление потоком предоставляется планировщику для завершения выполнения. От планировщика зависит то, следует ли запускать поток;
  3. Выполняемый: с началом выполнения потока его состояние изменяется на «выполняемый». Планировщик выбирает один поток из пула потоков и начинает его выполнение в приложении;
  4. Ожидающий: поток ожидает своего выполнения. Поскольку в приложении выполняется сразу несколько потоков, необходимо синхронизировать их. Следовательно, один поток должен ожидать, пока другой поток не будет выполнен. Таким образом, это состояние называется состоянием ожидания;
  5. Остановленный: выполняемый поток после завершения процесса переходит в состояние «остановленный», известное также как «мертвый».

Часто используемые методы для управления многопоточностью Java:

МетодОписание
start()Этот метод запускает выполнение потока, а JVM (виртуальная машина Java) вызывает в потоке метод Run ().
Sleep(int milliseconds)Делает поток спящим. Его выполнение будет приостановлено на указанное количество миллисекунд, после чего он снова начнет выполняться. Этот метод полезен при синхронизации потоков.
getName()Возвращает имя потока.
setPriority(int newpriority)Изменяет приоритет потока.
yield ()Останавливает текущий поток и запускает другие.
 

Например: В этом примере создается поток, и применяются перечисленные выше методы.

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 Автоматически сгенерированный блок catch
            e.printStackTrace();
        }
        guruthread1.setPriority(1);
        int gurupriority = guruthread1.getPriority();
        System.out.println(gurupriority);
        System.out.println("Thread Running");
  }
}

Объяснение кода

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

042616_0819_Multithread2

Вывод

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

Синхронизация потоков Java

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

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

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

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

Synchronized(object)
{  
        //Блок команд для синхронизации
}

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

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

Пример 1

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: выводим текст "Thread names are following:".
Строка 14: получаем имя потока 1, используя метод getName() класса thread.
Строка кода 15: получаем имя потока 2, используя метод getName() класса thread.

042616_0819_Multithread3

Вывод

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

  • Guru1
  • Guru2

Пример 2

Из этого Java многопоточности урока мы узнаем о переопределяющих методах Run () и методе Start () интерфейса runnable. Создадим два потока этого класса и выполним их.

Также мы задействуем два класса:

  • Один будет реализовывать интерфейс runnable;
  • Другой - с методом main и будет выполняться.
package demotest;
public class GuruThread2 {

 public static void main(String[] args) {
  // TODO Автоматически сгенерированный метод 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", содержащий метод main.
Строка 4: принимаем основной метод класса.
Строки 6—7: создаем экземпляр класса GuruThread3 (создается в строках внизу) как "threadguru1" и запускаем поток.
Строки 8—9: создаем еще один экземпляр класса GuruThread3 (создается в строках внизу) как "threadguru2" и запускаем поток.
Строка 11: для многопоточности Java создаем класс "GuruThread3", который реализует интерфейс «Runnable». Он должен быть реализован любым классом, экземпляры которого предназначены для выполнения потоком.
Строки 13—14: принимаем две переменные класса, из которых одна — потоковый класс, другая — строковый класс.
Строки 15—18: переопределение конструктора GuruThread3, который принимает один аргумент как тип String (являющийся именем потока). Имя будет присвоено переменной класса guruname и сохраняется имя потока.
Строка 20: переопределяется метод run() интерфейса runnable.
Строка 21: выводится имя потока с использованием набора команд println.
Строки 22—31: используется цикл «for» со счетчиком, инициализированным на «0», который не должен быть меньше 4. Выводится имя потока, а также выполняется приостановка потока на 1000 миллисекунд в блоке try-catch, поскольку метод sleep вызвал проверяемое исключение.
Строка 33: переопределяется метод start интерфейса runnable.
Строка 35: выводится текст "Thread started".
Строки 36—40: проверяем, содержит ли переменная класса guruthread значение. Если оно равно NULL, создается экземпляр класса thread. После этого запускается поток с использованием класса start().

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

042616_0819_Multithread4

Вывод

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

Получаем соответствующие имена потоков.

Выполняется цикл, в котором печатается счетчик и имя потока, а счетчик начинается с «0».

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

Следовательно, сначала мы получаем guru1, затем guru2 и снова guru2, поскольку процесс задерживается на 1000 миллисекунд, а дальше guru1 и снова guru1. Процесс снова задерживается на 1000 миллисекунд, после чего мы получаем guru2, а затем guru1.

Итог

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

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

- Новый;
- Готовый к выполнению;
- Выполняемый;
- Ожидающий;
- Остановленный.

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

Перевод статьи “Multithreading in Java Tutorial with Examples” был подготовлен дружной командой проекта Сайтостроение от А до Я.

03 октября 2017 в 11:30
Материалы по теме
{"url":"http://www.fastvps.ru/", "src":"/images/advbanners/fastvps.png", "alt":"Хостинг Fastvps.ru. Наш выбор!"}
Заработок