Будь-яка програма може мати кілька процесів (екземплярів). Кожен із цих процесів може бути призначений як єдиним потоком, так і кількома потоками. У цьому посібнику ми побачимо, як виконувати кілька завдань одночасно, а також дізнаємось більше про потоки та синхронізацію між потоками.
У цьому підручнику ми дізнаємось:
- Що таке одинарна нитка
- Що таке багатопоточність у Java?
- Життєвий цикл нитки в Java
- Синхронізація потоків Java
- Приклад багатопоточності Java
Що таке одинарна нитка?
Одна нитка - це в основному легка і найменша одиниця обробки. Java використовує потоки за допомогою "Thread Class".
Існує два типи потоків - потоки користувача та потоки демона (потоки демона використовуються, коли ми хочемо очистити додаток, і використовуються у фоновому режимі).
Коли програма починається вперше, створюється потік користувача. Опублікувавши це, ми можемо створити безліч потоків користувачів та потоки демонів.
Приклад однієї нитки:
демотест пакету;публічний клас GuruThread{public static void main (String [] args) {System.out.println ("Одиночна нитка");}}
Переваги однієї нитки:
- Зменшує накладні витрати в додатку, оскільки однопотокове виконання в системі
- Крім того, це зменшує витрати на обслуговування програми.
Що таке багатопоточність у Java?
MULTITHREADING в Java - це процес виконання двох або більше потоків одночасно для максимального використання центрального процесора. Багатопотокові програми виконують два або більше потоків, що працюють одночасно. Отже, він також відомий як паралельність у Java. Кожна нитка проходить паралельно одна одній. Багато потоків не виділяють окрему область пам'яті, отже, вони економить пам'ять. Крім того, перемикання контексту між потоками займає менше часу.
Приклад мультипотоку:
демотест пакету;публічний клас GuruThread1 реалізує Runnable{public static void main (String [] args) {Thread guruThread1 = new Thread ("Guru1");Thread guruThread2 = новий Thread ("Guru2");guruThread1.start ();guruThread2.start ();System.out.println ("Назви потоків такі:");System.out.println (guruThread1.getName ());System.out.println (guruThread2.getName ());}@Overridepublic void run () {}}
Переваги багатопоточності:
- Користувачів не блокують, оскільки потоки незалежні, і ми можемо виконувати кілька операцій одночасно
- Оскільки такі потоки незалежні, інші потоки не постраждають, якщо один потік відповідає винятку.
Життєвий цикл нитки в Java
Життєвий цикл нитки:
Існують різні етапи життєвого циклу нитки, як показано на схемі вище:
- Новий
- Біговий
- Біг
- Чекаю
- Мертві
- Нове: На цьому етапі потік створюється за допомогою класу "Thread class". Він залишається в такому стані, поки програма не запустить потік. Він також відомий як природжена нитка.
- Запуск: На цій сторінці екземпляр потоку викликається методом запуску. Керування потоком надається планувальнику для завершення виконання. Від планувальника залежить, чи запускати потік.
- Запуск: Коли потік починає виконуватися, стан змінюється на "запущений". Планувальник вибирає один потік із пулу потоків, і він починає виконуватися в додатку.
- Очікування: Це стан, коли потік повинен чекати. Оскільки в програмі запущено кілька потоків, існує потреба у синхронізації між потоками. Отже, один потік повинен зачекати, поки інший потік не буде виконаний. Тому цей стан називають станом очікування.
- Мертвий: це стан, коли потік завершується. Потік знаходиться в робочому стані, і як тільки він завершив обробку, він перебуває в "мертвому стані".
Деякі з найбільш часто використовуваних методів для потоків:
Метод | Опис |
---|---|
start () | Цей метод запускає виконання потоку, і JVM викликає метод run () для потоку. |
Сон (в мілісекундах) | Цей метод робить потік сплячим, отже, виконання потоку призупиняється на надані мілісекунди, а після цього потік знову починає виконуватися. Це допомагає у синхронізації потоків. |
getName () | Він повертає ім’я потоку. |
setPriority (int newpriority) | Це змінює пріоритет потоку. |
yield () | Це призводить до виконання поточного потоку на зупинці та інших потоків. |
Приклад: У цьому прикладі ми збираємося створити потік та дослідити вбудовані методи, доступні для потоків.
демотест пакету;відкритий клас thread_example1 реалізує Runnable {@Overridepublic void run () {}public static void main (String [] args) {Потік guruthread1 = новий Thread ();guruthread1.start ();спробуй {guruthread1.sleep (1000);} catch (InterruptedException e) {// TODO Автогенерований блок ловуe.printStackTrace ();}guruthread1.setPriority (1);int gurupriority = guruthread1.getPriority ();System.out.println (gurupriority);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 - це пріоритет Thread, а Thread Running - текст, який є результатом нашого коду.
Синхронізація потоків Java
У багатопотоковості існує асинхронна поведінка програм. Якщо один потік пише деякі дані, а інший потік, який одночасно зчитує дані, це може створити невідповідність програмі.
Коли є необхідність отримати доступ до спільних ресурсів за допомогою двох або більше потоків, тоді застосовується підхід синхронізації.
Java надала синхронізовані методи для реалізації синхронізованої поведінки.
При цьому підході, як тільки потік потрапляє всередину синхронізованого блоку, тоді жоден інший потік не може викликати цей метод на тому самому об’єкті. Усі потоки повинні чекати, поки цей потік закінчить синхронізований блок і вийде з нього.
Таким чином, синхронізація допомагає в багатопотоковій програмі. Один потік повинен зачекати, поки інший потік закінчить своє виконання, лише тоді інші потоки можуть бути виконані.
Це можна записати у такій формі:
Синхронізовано (об’єкт){// Блок операторів, що підлягає синхронізації}
Приклад багатопоточності Java
У цьому прикладі ми візьмемо два потоки та отримаємо імена потоку.
Приклад1:
GuruThread1.javaдемотест пакету;публічний клас GuruThread1 реалізує Runnable {/ *** @param аргументи* /public static void main (String [] args) {Thread guruThread1 = new Thread ("Guru1");Thread guruThread2 = новий Thread ("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 () інтерфейсу, який можна запустити, і створимо два потоки цього класу та запустимо їх відповідно.
Крім того, ми проходимо два заняття,
- Той, який реалізує запущений інтерфейс і
- Інший, який буде мати основний метод і виконуватиметься відповідно.
демотест пакету;публічний клас GuruThread2 {public static void main (String [] args) {// Автоматично згенерований заглушок TODOGuruThread3 threadguru1 = новий GuruThread3 ("guru1");threadguru1.start ();GuruThread3 threadguru2 = новий GuruThread3 ("guru2");threadguru2.start ();}}клас GuruThread3 реалізує Runnable {Нитка guruthread;приватний рядок guruname;GuruThread3 (ім'я рядка) {гурунаме = ім'я;}@Overridepublic void run () {System.out.println ("Потік запущений" + гуру-ім'я);для (int i = 0; i <4; i ++) {System.out.println (i);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 та те, як використовувати одинарні та багатопотокові потоки.
- У багатопотоковості користувачі не блокуються, оскільки потоки незалежні і можуть виконувати кілька операцій одночасно
- Різними стадіями життєвого циклу нитки є,
- Новий
- Біговий
- Біг
- Чекаю
- Мертві
- Ми також дізналися про синхронізацію між потоками, які допомагають додатку працювати безперебійно.
- Багатопотоковість полегшує набагато більше додаткових завдань.