Многопоточность в Java Tutorial с программой и примерами
Любое приложение может иметь несколько процессов (экземпляров). Каждый из этих процессов может быть назначен либо как один поток, либо как несколько потоков. В этом руководстве мы увидим, как выполнять несколько задач одновременно, а также узнаем больше о потоках и синхронизации между потоками.
В этом руководстве по многопоточности в Java мы узнаем:
- Что такое один поток
- Что такое многопоточность в Java?
- Жизненный цикл потока в Java
- Синхронизация потоков Java
- Пример многопоточности в Java
Что такое один поток?
Один поток в Java — это, по сути, легкая и наименьшая единица обработки. Java использует потоки с помощью «класса потока».
Существует два типа потоков:пользовательский поток и поток демона . (потоки демона используются, когда мы хотим очистить приложение и используются в фоновом режиме).
Когда приложение запускается впервые, создается пользовательский поток. После этого мы можем создать много пользовательских потоков и потоков демонов.
Пример одного потока:
package demotest; public class GuruThread { public static void main(String[] args) { System.out.println("Single Thread"); } }
Преимущества одного потока:
- Снижает нагрузку на приложение, так как в системе выполняется один поток.
- Кроме того, это снижает стоимость обслуживания приложения.
Что такое многопоточность в Java?
Многопоточность в Java — это процесс одновременного выполнения двух или более потоков для максимального использования ЦП. Многопоточные приложения выполняют два или более потоков одновременно. Следовательно, он также известен как Concurrency в 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
рисунок>центр>
Существуют различные этапы жизненного цикла потока, как показано на диаграмме выше:
- Новое
- Запускается
- Бег
- Ожидание
- Мертв
- Новое: На этом этапе поток создается с использованием класса «Класс потока». Он остается в этом состоянии до запуска программы. нить. Его также называют родившейся нитью.
- Запуск: На этой странице экземпляр потока вызывается методом запуска. Управление потоком передается планировщику для завершения выполнения. Запускать ли поток зависит от планировщика.
- Выполняется: Когда поток начинает выполняться, его состояние меняется на «выполняется». Планировщик выбирает один поток из пула потоков, и он начинает выполняться в приложении.
- Ожидание: Это состояние, когда поток должен ждать. Поскольку в приложении работает несколько потоков, необходима синхронизация между потоками. Следовательно, один поток должен ждать, пока другой поток не будет выполнен. Поэтому это состояние называется состоянием ожидания.
- Мертв: Это состояние, когда поток завершается. Поток находится в рабочем состоянии, и как только он завершил обработку, он находится в «мертвом состоянии».
Некоторые из наиболее часто используемых методов для потоков:
Метод | Описание |
---|---|
start() | Этот метод запускает выполнение потока, и JVM вызывает для потока метод run(). |
Спящий режим (целые миллисекунды) | Этот метод переводит поток в спящий режим, поэтому выполнение потока приостанавливается на предоставленные миллисекунды, после чего поток снова начинает выполняться. Это помогает в синхронизации потоков. |
getName() | Возвращает имя потока. |
setPriority(int newpriority) | Изменяет приоритет потока. |
выход () | Приводит к остановке текущего потока и выполнению других потоков. |
Пример: В этом примере многопоточной программы на 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: Здесь мы используем метод «спящего» потока с использованием экземпляра «guruthread1». Следовательно, поток будет бездействовать в течение 1000 миллисекунд.
- Код 9–14: Здесь мы поместили метод сна в блок try catch, так как возникает проверенное исключение, т. е. прерываемое исключение.
- Строка кода 15: Здесь мы устанавливаем приоритет потока равным 1 из любого приоритета, который был раньше
- Строка кода 16: Здесь мы получаем приоритет потока с помощью getPriority()
- Строка кода 17: Здесь мы печатаем значение, полученное из getPriority
- Строка кода 18: Здесь мы пишем текст о том, что поток запущен.
Когда вы выполняете приведенный выше код, вы получаете следующий вывод:
Вывод:
5 – это приоритет потока, а Thread Running – текст, который выводит наш код.
Синхронизация потоков Java
В многопоточности существует асинхронное поведение программ. Если один поток записывает некоторые данные, а другой поток одновременно считывает данные, это может привести к несогласованности в приложении.
Когда есть необходимость получить доступ к общим ресурсам двумя или более потоками, используется метод синхронизации.
Java предоставляет синхронизированные методы для реализации синхронизированного поведения.
При таком подходе, как только поток попадает внутрь синхронизированного блока, ни один другой поток не может вызвать этот метод для того же объекта. Все потоки должны ждать, пока этот поток завершит синхронизированный блок и выйдет из него.
Таким образом, синхронизация помогает в многопоточном приложении. Один поток должен ждать, пока другой поток завершит свое выполнение, только после этого другим потокам разрешено выполнение.
Его можно записать в следующем виде:
Synchronized(object) { //Block of statements to be synchronized }
Пример многопоточности в 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: Вывод текста как «Имена потоков следующие:»
- Строка кода 14: Получение имени потока 1 с помощью метода getName() класса потока.
- Строка кода 15: Получение имени потока 2 с помощью метода getName() класса потока.
Когда вы выполняете приведенный выше код, вы получаете следующий вывод:
Вывод:
Имена потоков выводятся здесь как
- Гуру1
- Гуру2
Пример 2:
В этом примере многопоточности в Java мы узнаем о переопределении методов run() и start() запускаемого интерфейса, а также создадим два потока этого класса и запустим их соответственно.
Кроме того, мы посещаем два занятия,
- Тот, который реализует исполняемый интерфейс и
- Еще один, который будет иметь метод main и выполняться соответствующим образом.
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 или нет. Если его значение null, то мы создаем экземпляр, используя класс потока, который принимает имя в качестве параметра (значение для которого было присвоено в конструкторе). После чего поток запускается методом start().
Когда вы выполняете приведенный выше код, вы получаете следующий вывод:
Вывод :
Следовательно, есть два потока, мы дважды получаем сообщение «Поток запущен».
Мы получаем имена потоков в том виде, в каком мы их вывели.
Он входит в цикл for, где мы печатаем счетчик и имя потока, а счетчик начинается с 0.
Цикл выполняется три раза, а между ними поток приостанавливается на 1000 миллисекунд.
Следовательно, сначала мы получаем guru1, затем guru2, затем снова guru2, потому что здесь поток спит 1000 миллисекунд, а затем следующий guru1 и снова guru1, поток спит 1000 миллисекунд, поэтому мы получаем guru2, а затем guru1.
Обзор
В этом руководстве мы рассмотрели многопоточные приложения на Java и способы использования одно- и многопоточных приложений в Java.
- Объясните многопоточность в Java:при многопоточности пользователи не блокируются, поскольку потоки независимы и могут одновременно выполнять несколько операций.
- Различные этапы жизненного цикла потока:
- Новое
- Запускается
- Бег
- Ожидание
- Мертв
- Мы также узнали о синхронизации между потоками, которая помогает приложению работать бесперебойно.
- Многопоточное программирование на Java упрощает выполнение многих прикладных задач.
java
- Java-программа Hello World
- Функции C++ с примерами программ
- Учебное пособие по коллекциям C# с примерами
- Метод Java String indexOf() с подстрокой и примерами
- Метод Java String charAt() с примером
- Метод Java String compareTo():как использовать с примерами
- Перегрузка конструктора в Java:что это такое и примеры программ
- Java-программа для проверки простого числа
- Алгоритм сортировки вставками в Java с примером программы
- Сортировка выбором в программе Java с примером