Любое приложение может иметь несколько процессов (экземпляров). Каждый из этих процессов может быть назначен как один поток, так и как несколько потоков. В этом руководстве мы увидим, как выполнять несколько задач одновременно, а также узнаем больше о потоках и синхронизации между потоками.
В этом уроке мы узнаем:
- Что такое однопоточный
- Что такое многопоточность в Java?
- Жизненный цикл потока в Java
- Синхронизация потоков Java
- Пример многопоточности Java
Что такое однопоточный?
Одиночный поток - это, по сути, легкая и самая маленькая единица обработки. Java использует потоки, используя «класс потока».
Существует два типа потоков - поток пользователя и поток демона (потоки демона используются, когда мы хотим очистить приложение, и используются в фоновом режиме).
Когда приложение запускается впервые, создается пользовательский поток. Опубликуйте это, мы можем создать множество пользовательских потоков и потоков демонов.
Пример одиночной резьбы:
демотест пакета;открытый класс GuruThread{public static void main (String [] args) {System.out.println («Однопоточный»);}}
Преимущества одинарной резьбы:
- Снижает накладные расходы в приложении, поскольку в системе выполняется один поток
- Кроме того, это снижает стоимость обслуживания приложения.
Что такое многопоточность в Java?
МУЛЬТИТРЕЙДИНГ в Java - это процесс одновременного выполнения двух или более потоков с максимальной загрузкой ЦП. Многопоточные приложения выполняют два или более потока одновременно. Следовательно, он также известен как параллелизм в Java. Каждый поток работает параллельно друг другу. Несколько потоков не выделяют отдельную область памяти, поэтому они экономят память. Кроме того, переключение контекста между потоками занимает меньше времени.
Пример многопоточности:
демотест пакета;открытый класс GuruThread1 реализует Runnable{public static void main (String [] args) {Тема guruThread1 = новая тема ("Guru1");Тема guruThread2 = новая тема ("Guru2");guruThread1.start ();guruThread2.start ();System.out.println ("Имена потоков следующие:");System.out.println (guruThread1.getName ());System.out.println (guruThread2.getName ());}@Overridepublic void run () {}}
Преимущества многопоточности:
- Пользователи не заблокированы, потому что потоки независимы, и мы можем время от времени выполнять несколько операций.
- Таким образом, потоки независимы, другие потоки не пострадают, если один поток встретит исключение.
Жизненный цикл потока в Java
Жизненный цикл потока:
Как показано на диаграмме выше, существуют различные этапы жизненного цикла потока:
- Новый
- Работоспособен
- Бег
- Ожидающий
- мертв
- Новое: на этом этапе поток создается с использованием класса "Thread class". Он остается в этом состоянии до тех пор, пока программа не запустит поток. Он также известен как прирожденная нить.
- Runnable: на этой странице экземпляр потока вызывается с помощью метода start. Управление потоком передается планировщику для завершения выполнения. Запускать ли поток зависит от планировщика.
- Выполняется: когда поток начинает выполняться, состояние изменяется на состояние «выполняется». Планировщик выбирает один поток из пула потоков, и он начинает выполнение в приложении.
- Ожидание: это состояние, когда поток должен ждать. Поскольку в приложении выполняется несколько потоков, существует необходимость в синхронизации между потоками. Следовательно, один поток должен ждать, пока другой поток не будет выполнен. Поэтому это состояние называется состоянием ожидания.
- Мертвый: это состояние, когда поток завершен. Поток находится в рабочем состоянии, и как только он завершил обработку, он находится в «мертвом состоянии».
Некоторые из наиболее часто используемых методов для потоков:
Методика | Описание |
---|---|
Начните() | Этот метод запускает выполнение потока, а JVM вызывает метод run () в потоке. |
Сон (целое число миллисекунд) | Этот метод переводит поток в спящий режим, поэтому выполнение потока приостанавливается на заданные миллисекунды, после чего поток снова начинает выполняться. Это помогает в синхронизации потоков. |
getName () | Возвращает имя потока. |
setPriority (int newpriority) | Это изменяет приоритет потока. |
урожай () | Это вызывает остановку текущего потока и выполнение других потоков. |
Пример: В этом примере мы собираемся создать поток и изучить встроенные методы, доступные для потоков.
демотест пакета;открытый класс thread_example1 реализует Runnable {@Overridepublic void run () {}public static void main (String [] args) {Тема guruthread1 = новая тема ();guruthread1.start ();пытаться {guruthread1.sleep (1000);} catch (InterruptedException e) {// TODO Автоматически сгенерированный блок catche.printStackTrace ();}guruthread1.setPriority (1);int gurupriority = guruthread1.getPriority ();System.out.println (приоритетность);System.out.println («Выполнение потока»);}}
Расшифровка кода:
- Строка кода 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 - это приоритет потока, а выполнение потока - это текст, который является выводом нашего кода.
Синхронизация потоков Java
В многопоточности наблюдается асинхронное поведение программ. Если один поток записывает некоторые данные, а другой поток одновременно читает данные, это может создать несогласованность в приложении.
Когда есть потребность в доступе к общим ресурсам для двух или более потоков, используется подход синхронизации.
Java предоставила синхронизированные методы для реализации синхронизированного поведения.
В этом подходе, как только поток достигает синхронизированного блока, никакой другой поток не может вызвать этот метод для того же объекта. Все потоки должны ждать, пока этот поток закончит синхронизированный блок и выйдет из него.
Таким образом, синхронизация помогает в многопоточном приложении. Один поток должен ждать, пока другой поток завершит свое выполнение, только тогда другие потоки будут разрешены для выполнения.
Это можно записать в следующем виде:
Синхронизированный (объект){// Блок операторов для синхронизации}
Пример многопоточности Java
В этом примере мы возьмем два потока и получим имена потоков.
Пример1:
GuruThread1.javaдемотест пакета;открытый класс GuruThread1 реализует Runnable {/ *** @param args* /public static void main (String [] args) {Тема guruThread1 = новая тема ("Guru1");Тема guruThread2 = новая тема ("Guru2");guruThread1.start ();guruThread2.start ();System.out.println ("Имена потоков следующие:");System.out.println (guruThread1.getName ());System.out.println (guruThread2.getName ());}@Overridepublic 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:
В этом примере мы узнаем о переопределении методов run () и start () исполняемого интерфейса, а также создадим два потока этого класса и запустим их соответственно.
Также мы берем два класса,
- Тот, который будет реализовывать исполняемый интерфейс и
- Еще один, у которого будет основной метод и который будет выполняться соответственно.
демотест пакета;public class GuruThread2 {public static void main (String [] args) {// TODO Заглушка автоматически сгенерированного методаGuruThread3 threadguru1 = новый GuruThread3 ("гуру1");threadguru1.start ();GuruThread3 threadguru2 = новый GuruThread3 ("гуру2");threadguru2.start ();}}class GuruThread3 реализует Runnable {Тема guruthread;личное String guruname;GuruThread3 (имя строки) {guruname = имя;}@Overridepublic void run () {System.out.println ("Поток запущен" + guruname);for (int i = 0; i <4; i ++) {System.out.println (я);System.out.println (имя гуру);пытаться {Thread.sleep (1000);} catch (InterruptedException e) {System.out.println («Поток был прерван»);}}}public void start () {System.out.println («Поток запущен»);if (guruthread == null) {guruthread = новая тема (это, 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 ().
Когда вы выполняете приведенный выше код, вы получаете следующий результат:
Выход :
Имеется два потока, следовательно, мы получаем два раза сообщение «Тема запущена».
Мы получаем имена потока в том виде, в котором они были выведены.
Он переходит в цикл for, где мы печатаем счетчик и имя потока, а счетчик начинается с 0.
Цикл выполняется трижды, и между ними поток находится в спящем состоянии на 1000 миллисекунд.
Следовательно, сначала мы получаем guru1, затем guru2, затем снова guru2, потому что поток спит здесь 1000 миллисекунд, затем следующий guru1 и снова guru1, поток спит 1000 миллисекунд, поэтому мы получаем guru2, а затем guru1.
Резюме :
В этом руководстве мы увидели многопоточные приложения на Java и то, как использовать однопоточные и многопоточные.
- В многопоточности пользователи не блокируются, поскольку потоки независимы и могут выполнять несколько операций одновременно.
- Различные этапы жизненного цикла потока:
- Новый
- Работоспособен
- Бег
- Ожидающий
- мертв
- Мы также узнали о синхронизации между потоками, которая помогает приложению работать без сбоев.
- Многопоточность упрощает многие прикладные задачи.