Багатопотоковість в Java
Будь-яка програма може мати кілька процесів (примірників). Кожному з цих процесів можна призначити один або декілька потоків. У цьому посібнику ми побачимо, як виконувати кілька завдань одночасно, а також дізнаємося більше про потоки та синхронізацію між потоками.
Що таке Single Thread?
Одна нитка в Java в основному є легкою та найменшою одиницею обробки. Java використовує потоки за допомогою «класу потоку». Існує два типи ниток – потік користувача та потік демона (потоки daemon використовуються, коли ми хочемо очистити програму, і використовуються у фоновому режимі). Коли програма вперше запускається, створюється потік користувача. Опублікувавши це, ми можемо створити багато потоків користувачів і потоків демонів.
Приклад одного потоку:
package demotest; public class GuruThread { public static void main(String[] args) { System.out.println("Single Thread"); } }
Переваги однієї нитки:
- Зменшує накладні витрати в програмі, оскільки в системі виконується один потік
- Крім того, це зменшує витрати на обслуговування програми.
Що таке багатопотоковість Java?
Багатопотоковість in Java це процес виконання двох або більше потоків одночасно для максимального використання ЦП. Багатопотокові програми виконують два або більше потоків, що виконуються одночасно. Тому він також відомий як Concurrency in 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
Життєвий цикл потоку:
Існують різні етапи життєвого циклу потоку, як показано на схемі вище:
- Нові
- Біговий
- Робота
- Очікування
- померлі
- Нове: На цьому етапі потік створюється за допомогою класу Thread class. Він залишається в цьому стані до програми починається нитка. Він також відомий як уроджена нитка.
- Запуск: На цій сторінці екземпляр потоку викликається за допомогою методу start. Керування потоком передається планувальнику для завершення виконання. Від планувальника залежить, чи запускати потік.
- Запуск: Коли потік починає виконуватися, стан змінюється на стан «виконується». Планувальник вибирає один потік із пулу потоків, і він починає виконуватися в програмі.
- Очікування: Це стан, коли потік має чекати. Оскільки в програмі працює кілька потоків, існує потреба в синхронізації між потоками. Отже, один потік повинен чекати, поки не буде виконано інший потік. Тому цей стан називають станом очікування.
- Мертві: Це стан, коли потік завершується. Потік знаходиться в стані виконання, і як тільки він завершив обробку, він перебуває в «мертвому стані».
Методи багатопоточності в 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: ми будемо використовувати метод «запуск» потоку, використовуючи екземпляр «guruthread1». Тут потік почне виконуватися.
- Рядок коду 10: Тут ми використовуємо метод «сплячого» потоку з використанням екземпляра «guruthread1». Таким чином, потік буде спати протягом 1000 мілісекунд.
- Код 9-14: Тут ми помістили метод сну в блок try catch, оскільки виникає перевірена виняткова ситуація, тобто перервана виняткова ситуація.
- Рядок коду 15: Тут ми встановлюємо пріоритет потоку на 1 незалежно від того, який пріоритет він був
- Рядок коду 16: Тут ми отримуємо пріоритет потоку за допомогою getPriority()
- Рядок коду 17: Тут ми друкуємо значення, отримане з getPriority
- Рядок коду 18: Тут ми пишемо текст, який виконується.
Коли ви виконуєте наведений вище код, ви отримуєте такий результат:
вихід:
5 — це пріоритет Thread, а Thread Running — це текст, який є результатом нашого коду.
Java Нитка Syncхронізація
У багатопоточності існує асинхронна поведінка програм. Якщо один потік записує деякі дані, а інший потік одночасно читає дані, це може створити неузгодженість у програмі. Коли є потреба отримати доступ до спільних ресурсів через два або більше потоків, тоді використовується підхід синхронізації. Java надав синхронізовані методи для реалізації синхронізованої поведінки.
У цьому підході, як тільки потік потрапляє всередину синхронізованого блоку, жоден інший потік не може викликати цей метод для того самого об’єкта. Усі потоки повинні чекати, поки цей потік завершить синхронізований блок і вийде з нього. Таким чином, синхронізація допомагає в багатопоточних програмах. Один потік повинен чекати, поки інший потік завершить своє виконання, тільки тоді інші потоки можуть виконуватися.
Його можна записати в такій формі:
Synchronized(object) { //Block of statements to be synchronized }
Багатопотоковість в Java Приклади програм
При цьому багатопотоковість Java наприклад, ми візьмемо два потоки та отримаємо імена потоку.
Example1:
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() класу потоку.
Коли ви виконуєте наведений вище код, ви отримуєте такий результат:
вихід:
Назви потоків виводяться тут як
- Guru1
- Guru2
Приклад 2:
У цій багатопоточності в Java Наприклад, ми дізнаємося про перевизначення методів run() і start() інтерфейсу, що виконується, і створимо два потоки цього класу та запустимо їх відповідно.
Крім того, ми відвідуємо два класи,
- Такий, який реалізовуватиме запущений інтерфейс і
- Ще один, який матиме основний метод і виконуватиметься відповідно.
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: Виводимо текст «Thread started».
- Рядок коду 36-40: Тут ми використовуємо умову if, щоб перевірити, чи має змінна класу guruthread значення чи ні. Якщо значення null, ми створюємо екземпляр за допомогою класу потоку, який приймає ім’я як параметр (значення для якого було призначено в конструкторі). Після цього потік запускається за допомогою методу start().
Коли ви виконуєте наведений вище код, ви отримуєте такий результат:
Вихід:
Є два потоки, отже, ми отримуємо два рази повідомлення «Потік розпочато».
Ми отримуємо назви потоку, як ми їх вивели.
Він переходить у цикл for, де ми друкуємо лічильник і назву потоку, а лічильник починається з 0.
Цикл виконується тричі, а між ними потік перебуває в режимі сну на 1000 мілісекунд.
Отже, спочатку ми отримуємо guru1, потім guru2, потім знову guru2, тому що потік тут спить протягом 1000 мілісекунд, а потім наступний guru1 і знову guru1, потік спить протягом 1000 мілісекунд, тому ми отримуємо guru2, а потім guru1.
Підсумки
У цьому підручнику ми бачили багатопотокові програми Java і як використовувати один і багато потоків у Java.
- Поясніть багатопотоковість в Java: у багатопоточності користувачі не блокуються, оскільки потоки є незалежними та можуть виконувати декілька операцій одночасно
- Різні етапи життєвого циклу потоку:
- Нові
- Біговий
- Робота
- Очікування
- померлі
- Ми також дізналися про синхронізація між потоками, що сприяє безперебійній роботі програми.
- Багатопотокове програмування в Java полегшує виконання багатьох інших прикладних завдань.