Багатозадачність. Потоки виконання (Threads). Основні поняття
У сучасних операційних системах реалізовано механізм багатозадачності. За допомогою цього механізму в операційній системі може одночасно виконуватися кілька програм (завдань). Наприклад, можна набирати текст програми на мові Java, яка використовує потоки та одночасно слухати музику Бетховена.
Розрізняють два види багатозадачності:
- багатозадачність на основі процесів - це засіб паралельного виконання двох або більше програм на комп'ютері;
- багатозадачність на основі потоків - це засіб розпаралелювання виконання коду всередині однієї окремої програми (процесу).
Основна перевага використання багатозадачності – зменшення часу очікування виконання кількох завдань. Зменшення часу очікування у сучасних інтерактивних середовищах дозволяє максимально використовувати доступні обчислювальні потужності у системі.
2. Багатозадачність з урахуванням процесів. Загальні поняття
При використанні багатозадачності на основі процесів важливим поняттям є процес. Процес — це окрема програма, що виконується. Отже, багатозадачність з урахуванням процесів — це засіб паралельного виконання двох чи більше програм на комп'ютері. Паралельне виконання забезпечується процесором (декількома процесорами) таким чином, що кожній паралельно-виконуваній програмі виділяється певний квант часу (ресурсу) процесора. У цьому випадку програма вважається найменшою одиницею коду.
Наприклад, розробка програми з використанням компілятора мови Java є окремим процесом, на який немає впливу цієї програми;
Процеси є великими завданнями та потребують більше системних ресурсів. Кожному процесу виділяється свій окремий адресний простір. Зв'язок між процесами обмежена та ресурсомістка. Перемикання контексту (зміна активності) між процесами також потребує «великих зусиль» від процесора та операційної системи.
3. Багатозадачність з урахуванням потоків. Поняття потоку виконання
Багатозадачність на основі потоків ще називають багатопоточність. При використанні багатопоточності найменшою одиницею диспетчерського коду є потік виконання. Одна програма може виконувати кілька потоків (завдань) одночасно. Таким чином, багатозадачність на основі потоків - Це засіб розбиття коду на окремі паралельно-виконувані частини.
Наприклад, у текстовому редакторі можна друкувати документ одночасно з набором тексту в іншому документі.
Потоки виконання всередині процесів, порівняно з процесами, вимагають набагато менше системних ресурсів. Потоки спільно використовують єдиний адресний простір одного процесу, у якому вони розпаралелені. Зв'язок та перемикання контексту (зміна активності) між процесами порівняно слабко навантажує процесор та операційну систему.
4. Загальні відомості про потоки виконання. Багатопоточність
Мова програмування Java містить вбудовані кошти для реалізації багатопотокового програмування. За допомогою цих засобів існує можливість керувати так званими потоками виконання.
Виконання кожної програми може бути розбите на кілька потоків. Кожен потік виконання є окремим шляхом виконання коду. Кожен потік виконання створюється у головному потоці і називається дочірнім потоком. Після створення кількох потоків у програмі, головний потік та дочірні потоки:
- можуть тривати різний період;
- можуть завершуватися у різні моменти часу. Тут можлива ситуація коли головний потік завершується до завершення виконання дочірнього потоку.
p align="justify"> Для роботи з потоками виконання в мові Java реалізовані великі бібліотеки класів.
5. Види станів потоків виконання
Потоки виконання можуть бути в кількох станах:
- потік виконується;
- потік готовий до виконання;
- потік припинено;
- потік починає виконуватись після його зупинки;
- потік заблоковано. Це випадок, коли потік очікує на отримання деякого ресурсу;
- потік перервано. Перерваний потік може бути відновлено.
6. Пріоритети потоків. Основні поняття. Випадки перемикання контексту
З використанням потоків виконання виникає поняття пріоритету. Пріоритет — це ціле значення, яке визначає поведінку даного потоку по відношенню до інших потоків. Для кількох потоків числове значення пріоритету задає відносний пріоритет одного потоку над іншими. Якщо виконується лише один потік, то числова величина пріоритету не має значення. Пріоритет потоку враховується у випадках, коли одночасно виконуються кілька потоків та відбувається так зване перемикання контексту - Перехід від одного потоку до іншого.
Перехід від одного потоку до іншого виконується у таких випадках:
- випадок, коли потік з більшим пріоритетом витісняє виконання потоку з меншим пріоритетом незалежно від того, які операції виконує низькопріоритетний потік. Іншими словами, потік із великим пріоритетом отримує право на виконання за першої необхідності. Цей випадок демонструє так звану витісняючу багатозадачність;
- потік передає керування іншому потоку добровільно. У цьому випадку керування передається іншому потоку, який має найвищий пріоритет серед потоків, що виконуються в даний момент. Якщо добровільно потрібно передати управління потокам з однаковим пріоритетом, ці потоки по колу поділяють час центрального процесора.
7. Синхронізація потоків. Монітор
У багатозадачних середовищах потоки (програми) виконуються асинхронно. Асинхронність визначається хаотичним використанням наявних ресурсів виконуваним потоком.
Для двох (або більше) потоків, що виконуються паралельно, можуть виникати випадки, коли між ними потрібно організувати синхронізацію. Одним із прикладів необхідності застосування синхронізації є уникнення конфліктів між потоками за спільно використовувані ресурси. Синхронізація — це механізм взаємодії між двома (або більше) потоками з дотриманням певних правил, щоб уникнути колізій, одночасного чи неузгодженого використання спільних ресурсів тощо.
Для синхронізації в мові Java введено використання так званих моніторів. Монітор — це технологія синхронізації потоків, яка передбачає зберігання лише одного потоку виконання, якому надаються привілеї доступу до ресурсу. Отже, монітор забезпечує захист ресурсів від одночасного використання більш ніж одним потоком виконання.
При використанні технології монітора виникає таке поняття, яквхід потоку до монітора«. Якщо кільком потокам одночасно потрібно підійти до спільного ресурсу, доступ має тільки той потік, який увійшов у монітор. Всі інші потоки очікують доти, доки монітор не звільниться (потік, що увійшов до монітора, не залишить його).
На малюнку 1 наведено приклад роботи монітора для 4-х потоків. Вхід у монітор здійснює потік Thread3. Цей потік має доступом до загального ресурсу. Всі інші потоки очікують у черзі доти, поки потік Thread3 не залишить монітор.
Малюнок 1. Доступ до спільного ресурсу за допомогою монітора
Пов'язані теми
Взаємодія та синхронізація процесів та потоків
У мультипрограмних однопрограмних системах процеси чергуються, забезпечуючи ефективне виконання програм. У багатопроцесорних системах можливе як чергування, а й перекриття процесів. Обидві ці технології, які можна як приклади паралельних обчислень, породжують однакові проблеми. Виконання процесів та потоків у мультипрограмному середовищі завжди має асинхронний характер – неможливо передбачити відносну швидкість виконання процесів. Момент переривання потоків, час знаходження їх у чергах до ресурсів, порядок вибору потоків для виконання - всі ці події є результатом збігу багатьох обставин і є випадковими, це справедливо як по відношенню до потоків одного процесу, що виконує загальний програмний код, так і по відношенню до потоків різних процесів, кожен із яких виконує власну програму.
Способи взаємодії процесів (потоків) можна класифікувати за ступенем поінформованості одного процесу існування іншого [ 10 ].
- Процеси не обізнані наявність один одного (наприклад, процеси різних завдань одного або різних користувачів). Це незалежні процеси, які не призначені для спільної роботи. Хоча ці процеси і працюють спільно, ОС має вирішувати питання конкурентного використання ресурсів.Наприклад, дві незалежні програми можуть вимагати доступ до одного і того ж диска або принтера. ОС має регулювати такі звернення.
- Процеси побічно обізнані наявність один одного (наприклад, процеси одного завдання). Ці процеси не обов'язково повинні бути поінформовані про наявність один одного з точністю до ідентифікатора процесу, однак вони поділяють доступ до деякого об'єкта, наприклад, буфера виводу-введення, файлу або БД. Такі процеси демонструють співпрацю під час розподілу загального об'єкта.
- Процеси безпосередньо обізнані наявність один одного (наприклад, процеси, що працюють послідовно або по черзі в рамках одного завдання). Такі процеси здатні спілкуватися один з одним з використанням ідентифікаторів процесів та спочатку створені для спільної роботи. Ці процеси також демонструють співпрацю під час роботи.
Таким чином, потенційні проблеми, пов'язані із взаємодією та синхронізацією процесів та потоків, можуть бути представлені наступною таблицею.
- Результат роботи одного процесу залежить від дій інших.
- Можливий вплив одного процесу на час роботи іншого
- Взаємовиключення
- Взаємоблокування
- Голодування
- Результат роботи одного процесу може залежати від інформації, отриманої з інших.
- Можливий вплив одного процесу на час роботи іншого
- Взаємовиключення
- Взаємоблокування
- Голодування
- Синхронізація
- Результат роботи одного процесу залежить від інформації, отриманої з інших процесів.
- Можливий вплив одного процесу на час роботи іншого
- Взаємоблокування (витрати ресурси)
- Голодування
За необхідності використовувати той самий ресурс паралельні процеси входять у конфлікт (конкурують) друг з одним.Кожен із процесів не підозрює про наявність інших і не руйнується жодного впливу з їхнього боку. Звідси випливає, що кожен процес не повинен змінювати стан будь-якого ресурсу, з яким він працює. Прикладами таких ресурсів можуть бути пристрої введення-виведення, пам'ять, процесорний час, годинник.
Між конкуруючими процесами немає ніякого обміну інформацією. Однак виконання одного процесу може вплинути на поведінку конкуруючого процесу. Це може, наприклад, висловитися в уповільненні роботи одного процесу, якщо ОС виділить ресурс іншому процесу, оскільки перший процес чекатиме завершення роботи з цим ресурсом. В граничному випадку блокований процес може ніколи не отримати доступ до потрібного ресурсу і, отже, ніколи не зможе завершитись.
У разі конкуруючих процесів (потоків) можливе виникнення трьох проблем. Перша їх – необхідність взаємних винятків (mutual exclusion). Припустимо, що два або більше процесів вимагають доступ до одного нерозділюваного ресурсу, як принтер (рис. 5.12). Про такий ресурс говоритимемо як про критичний ресурс, а про частину програми, яка його використовує, – як про критичний розділ (critical section) програми. Вкрай важливо, щоб у критичній ситуації будь-якої миті могла перебувати лише одна програма. Наприклад, під час друку файлу потрібно, щоб окремий процес мав повний контроль за принтером, інакше на папері можна отримати чергування рядків двох файлів.
Мал. 5.12. Критична секція
Здійснення взаємних винятків створює дві додаткові проблеми. Одна з них – взаємоблокування (deadlock) або безвихідь.Розглянемо, наприклад, два процеси – P1 та P2, і два ресурси – R1 та R2. Припустимо, що кожному процесу виконання частини своїх функцій потрібен доступ до загальних ресурсів. Тоді можливе виникнення наступної ситуації: ОС виділяє ресурс R1 процесу Р2, а ресурс R2 процесу Р1. У результаті кожен процес очікує на отримання одного з двох ресурсів. При цьому жоден з них не звільняє вже наявний ресурс, очікуючи на одержання другого ресурсу для виконання функцій, що вимагають наявність двох ресурсів. В результаті процеси виявляються взаємно заблокованими.
Дуже зручно моделювати умови виникнення глухих кутів, використовуючи спрямовані графи [ 17 ] (Запропоновано Holt, 1972). Графи мають 2 види вузлів: процеси-кружечки та ресурси-квадратики. Ребро, спрямоване від квадрата (ресурсу) до кружка (процесу), означає, що ресурс був запрошений, отриманий та використовується. У прикладі це буде зображено так, як показано на рис. 5.13 а).
Ребро, спрямоване від процесу (кухля) до ресурсу (квадрату), означає, що процес в даний момент заблокований і знаходиться в стані очікування доступу до цього ресурсу. У прикладі граф треба добудувати, як показано на рис. 5.13 б) чи в). Цикл у графі означає наявність взаємного блокування процесів.
Існує ще одна проблема у конкуруючих процесів – голодування. Припустимо, що є 3 процеси (Р1, Р2, Р3), кожному з яких періодично потрібен доступ до ресурсів R. Уявімо ситуацію, в якій Р1 володіє ресурсом, а Р2 і Р3 призупинені в очікуванні звільнення ресурсу R. Після виходу Р1 з критичного розділу доступ до ресурсу буде отримано одним із процесів Р2 або Р3.
Мал. 5.13. Тупикова ситуація
Нехай ОС надала доступ до ресурсу процесу Р3. Поки він працює з ресурсом, доступ до ресурсу знову потрібний процесу Р1. В результаті зі звільнення ресурсу R процесом Р3 може виявитися, що ОС знову надасть доступ до ресурсу процесу Р1. Тим часом процесу Р3 знову потрібен доступ до ресурсу R. Таким чином, теоретично можлива ситуація, в якій процес Р2 ніколи не отримає доступ до необхідного ресурсу, незважаючи на те, що ніякого взаємного блокування в даному випадку немає.
Розглянемо випадок співробітництва з використанням розподілу. Цей випадок охоплює процеси (потоки), що взаємодіють з іншими (потоками), без наявності явної інформації про них. Наприклад, кілька потоків можуть звертатися до змінних (глобальних) або спільно використовуваних файлів або баз даних. Оскільки дані зберігаються в ресурсах (пристроях пам'яті), у цьому випадку також можливі проблеми взаємоблокування, взаємовиключення та голодування. Єдина відмінність у тому, що доступ до даних може здійснюватися у двох режимах – читання та запису, і взаємовиключними мають бути лише операції запису.
Однак у разі вноситься нове вимога синхронізації процесів задля забезпечення узгодженості даних.
Нехай є два процеси, представлені послідовністю неподільних (атомарних) операцій:
де a, b, c, d, e, f – атомарні операції.
При послідовному виконанні активностей ми отримуємо наступну послідовність атомарних процесів:
Що станеться у виконанні цих процесів псевдопараллельно, як поділу часу? Процеси можуть розшаруватися на неподільні операції з різним чергуванням, тобто може статися те, що англійською мовою прийнято називати словом interleaving. Можливі варіанти чергування:
В даному випадку атомарні операції активностей можуть чергуватись різними способами зі збереженням свого порядку розташування всередині процесів. Так як псевдопаралельне виконання двох процесів призводить до чергування їх неподільних операцій, результат псевдопаралельного виконання може відрізнятися від послідовного результату виконання. Нехай є два процеси P і Q, що складаються з двох атомарних операцій:
Що ми отримаємо в результаті їх псевдопаралельного виконання, якщо змінні x та y є загальними для процесів? Легко бачити, що можливі чотири різні набори значень для пари (x, y): (3, 4), (2, 1), (2, 3) та (3, 2). Будемо говорити, що набір процесів детермінований, якщо кожного разу при псевдопаралельному виконанні для того самого набору вхідних даних він дає однакові вихідні дані. Інакше він недетермінований. Вище наведено приклад недетермінованого набору програм. Зрозуміло, що детермінований набір активностей можна безбоязно виконувати як розділення часу. Для недетермінованого набору таке виконання небажане.
Чи можна до отримання результатів, заздалегідь визначити, чи є набір активностей детермінованим чи ні? Для цього існують достатні умови Бернстайна [ 17 ]. Викладемо їх стосовно програм з змінними, що розділяються.
Введемо набори вхідних та вихідних змінних програми.Для кожної атомарної операції набори вхідних та вихідних змінних – це набори змінних, які атомарна операція зчитує та записує. Набір вхідних змінних програми R(P) (R від слова read) суть об'єднання наборів вхідних змінних всім її неподільних дій. Аналогічно, набір вихідних змінних програми W(P) (W від слова write) є об'єднання наборів вихідних змінних для всіх її неподільних дій. Наприклад, для програми
отримуємо R(P) = W (P) = . Зауважимо, що змінна x присутня як R(P), так і W(P).
Тепер сформулюємо умови Бернстайна.
Якщо двох даних процесів P і Q:
- перетин W(P) і W(Q) порожньо,
- перетин W(P) з R(Q) порожньо,
- перетин R(P) і W(Q) порожньо,
тоді виконання P і Q детерміновано.
Якщо цих умов не дотримано, можливо, що паралельне виконання P і Q детерміновано, але можливо, що немає. Випадок двох процесів природно узагальнюється на їхню більшу кількість.
Умови Бернстайна інформативні, але надто жорсткі. По суті, вони вимагають практично невзаємодіючих процесів. Однак хотілося б, щоб детермінований набір утворював процеси, що спільно використовують інформацію та обмінюються нею. Для цього нам необхідно обмежити кількість можливих чергувань атомарних операцій, виключивши деякі чергування за допомогою механізмів синхронізації виконання програм та забезпечивши цим упорядкований доступ програм до деяких даних.
Про недетермінований набір програм говорять, що має race condition (стан гонки, стан змагання). У наведеному прикладі процеси змагаються за обчислення значень змінних x і y.
Завдання впорядкованого доступу до даних, що розділяються (усунення race condition), у тому випадку, якщо нам не важлива його черговість, можна вирішити, якщо забезпечити кожному процесу ексклюзивне право доступу до цих даних. Кожен процес, що звертається до ресурсів, що розділяється, виключає для всіх інших процесів можливість одночасного з ним спілкування з цими ресурсами, якщо це може призвести до недетермінованої поведінки набору процесів. Такий прийом називається взаємовиключенням (mutual exclusion). Якщо черговість доступу до ресурсів важлива для отримання правильних результатів, то одними взаємовиключеннями вже не обійтися.
При співпраці з використанням зв'язку різні процеси беруть участь у спільній роботі, що їх об'єднує. Зв'язок забезпечує можливість синхронізації, чи координації, різних процесів процесів. Зазвичай вважатимуться, що зв'язок складається з повідомлень певного виду. Приміти для надсилання та отримання повідомлень можуть бути надані мовою програмування або ядром операційної системи.
Оскільки в процесі передачі повідомлень немає будь-якого спільного використання ресурсів, взаємовиключення не потрібно, хоча проблеми взаємоблокувань і голодування залишаються актуальними. Як приклад взаємоблокування можна навести ситуацію, коли кожен із двох процесів заблокований очікуванням повідомлення від іншого процесу. Голодування можна проілюструвати в такий спосіб. Нехай є три процеси Р1, Р2, Р3, а ті, своєю чергою, намагаються зв'язатися з процесом Р1. Може виникнути ситуація, коли Р1 та Р2 постійно зв'язуються один з одним, а Р3 залишається заблокованим, очікуючи зв'язку з процесом Р1.
Сподобалася стаття? Додай її в закладку (CTRL+D) і не забудь поділитися з друзями:
☕🧵 Введення в багатопоточність у Java. Частина 1Переваги та недоліки багатопоточності
У чому полягає «магія» багатопоточності? Як створити потік і чим він відрізняється від процесу? Як процесор обробляє потоки?
Незалежно від того, яку мову ви використовуєте для написання своїх програм, за умовчанням усі вони є послідовними. Тобто всі інструкції, які ми пишемо, виконуються послідовно операційною системою. Наступний рядок коду не може розпочати виконання до завершення поточного і чекає, поки поточний рядок не завершить своє виконання. Якщо програма має виклик API до віддаленого сервера, програма блокується доти, доки не отримає відповідь до запиту. Якщо виконання виклику займає кілька хвилин, то нам доведеться чекати відповіді програми. Програма у такому разі просто блокується і не відповідатиме на подальші команди.
Давайте розглянемо інші поширені сценарії. Наприклад, ви використовуєте якусь соціальну мережу, де ви завантажуєте відео або зображення і одночасно спілкуєтеся там зі своїми друзями. Ви виконуєте дві різні завдання одночасно, і ваша програма не блокується, доки вона не завершить одне зі своїх завдань. Як? Тут у гру вступає багатопоточність.
Існує безліч визначень багатопоточності:
- Багатопоточність - Це здатність процесора незалежно виконувати процеси або потоки. Усі програми виконуються потоками. Потік – це легковажний підпроцес, найменша одиниця обробки.
- Багатопоточність - Це процес одночасного виконання декількох потоків.
Переваги багатопоточності
- з використанням багатопоточності можна розробляти більш адаптивні програми: ми можемо виконувати кілька операцій одночасно, наприклад, завантаження деяких ресурсів і спілкування в чаті одночасно;
- ми можемо досягти кращого використання ресурсів: за замовчуванням програма Java є однопотоковою.Можливо кілька ядер процесора, які можна використовувати, застосовуючи багатопоточність;
- загальна продуктивність може бути збільшена у кілька разів.
Недоліки багатопоточності
- потоки маніпулюють даними, розташованими в одній і тій же пам'яті, належать тому самому процесу, і необхідно забезпечувати синхронізацію та узгодженість даних між потоками;
- досить складно проектувати багатопотокові додатки і важко налагоджувати у разі помилок;
- коли потоків багато, процесору доводиться перемикатися між потоками. Цей процес називається перемиканням контексту. Переключення між потоками – дорога операція, оскільки процесор повинен зберігати локальні дані одного потоку та завантажувати локальні дані іншого потоку. Зрештою, загальна продуктивність постраждає, а не покращиться, якщо буде занадто багато потоків.
Багатопоточність у Java
Java надає базовий клас для створення потоків. Thread . Існує два способи створення потоків: або успадкування класу Thread та перевизначення методу run() , або реалізація інтерфейсу Runnable та передача його реалізації класу Thread як аргумент конструктора. Давайте розглянемо приклад із використанням обох способів.
Створення потоків шляхом успадкування класу Thread
public class App < public static void main(String[] args) < var ferrari = new Car("Ferrari"); var bmw = new Car("BMW"); ferrari.start(); bmw.start(); System.out.println("Method continues execution. Main method is executed by thread " + Thread.currentThread().getName()); >> class Car extends Thread < private final String model; public Car(String model) < this.model = model; >@Override public void run() < try < Thread.sleep(1000); >catch (InterruptedException exception) < exception.printStackTrace(); >System.out.println("Car " + model + " is being driven by thread " + Thread.currentThread().getName()); > >
У наведеному вище фрагменті коду ми створюємо клас Car , який успадковує клас Thread та перевизначає його метод run(). Усередині методу run() ми просто виводимо модель автомобіля та ім'я виконуваного потоку.
Thread.sleep(1000) - Зупиняє цей потік на заданий період часу (у мілісекундах). У main-методі ми створюємо два екземпляри (ferrari, bmw) класу Car і викликаємо метод start() кожному з них. Потім виводимо якесь повідомлення. За замовчуванням щоразу, коли запускається будь-яка Java-програма, вона виконується основним потоком. Запуск цієї програми дає такий висновок.
Як бачимо з висновку, висновок повідомлення, яке написали у методі main остання команда в програмі, але вона виводиться в консоль першою і не чекає виконання викликів методів. ferrari.start() і bmw.start() . Це і є магія багатопоточності. Скільки б часу не знадобилося для виконання методів ferrari.start() і bmw.start(), потік main далі виконується і чекає їх завершення.
Створення потоків шляхом реалізації інтерфейсу Runnable
public class App < public static void main(String[] args) < var ferrari = new Car("Ferrari"); var bmw = new Car("BMW"); var ferrariThread = новий Thread(ferrari, "Ferrari-Thread"); var bmwThread = новий Thread(bmw, "BMW-Thread"); ferrariThread.start(); bmwThread.start(); System.out.println("Method continues execution. Main method is executed by thread " + Thread.currentThread().getName()); >> class Car implements Runnable < private final String model; public Car(String model) < this.model = model; >@Override public void run() < try < Thread.sleep(1000); >catch (InterruptedException exception) < exception.printStackTrace(); >System.out.println("Car " + model + " is being driven by thread " + Thread.currentThread().getName()); > >
Наведений вище фрагмент коду виконує ту ж логіку, яку ми обговорювали вище, але він трохи відрізняється від попереднього фрагмента коду. У цьому випадку ми реалізуємо інтерфейс Runnable та перевизначаємо його метод run() та передаємо їх конструктору класу Thread . Один із конструкторів класу Thread приймає інтерфейс Runnable як один із своїх аргументів. Висновок програми аналогічний
Зважаючи на два способи створення потоків, другий вважається кращим, оскільки множинне спадкування заборонено в Java , і, успадкувавши клас Thread , ми не зможемо успадкувати будь-який інший клас. Однак, реалізувавши інтерфейс Runnable , ми зможемо успадкувати інший клас. Це невелика перевага другого способу.
🧩☕ Цікаві завдання Java для практики можна знайти на нашому телеграм-каналі «Бібліотека задач Java»
Що станеться, якщо ми викличемо метод run() класу Thread?
public class App < public static void main(String[] args) < var ferrari = new Car("Ferrari"); var bmw = new Car("BMW"); ferrari.run(); bmw.run(); System.out.println("Method continues execution. Main method is executed by thread - " + Thread.currentThread().getName()); >> class Car extends Thread < private final String model; public Car(String model) < this.model = model; >@Override public void run() < try < Thread.sleep(1000); >catch (InterruptedException exception) < exception.printStackTrace(); >System.out.println("Car " + model + " is being driven by thread " + Thread.currentThread().getName()); > >
Давайте розглянемо наведений вище фрагмент коду. У цьому прикладі аналогічно ми створили клас Car , який успадковує клас Thread , перевизначили метод run() , створили два екземпляри класу Car ( ferrari, bmw) . Замість виклику методу start() ми викликали метод run() у екземплярів (ferrari, bmw) . Запуск цієї програми дає наступний результат.
При виклику методу run() , програма виконується послідовно, у порядку, у якому ми написали. Виклик методу run() не створює новий потік, він веде себе як звичайний метод у Java .
Процеси та потоки
Терміни "процес" і "потік" повсюдно використовуються, коли йдеться про багатопоточність. Давайте докладно розглянемо ці дві концепції.
Процес — це програма, що виконується, або, іншими словами, просто запущена програма
- коли ви запускаєте якусь програму або веб-браузер, запускаються різні процеси;
- операційна система призначає кожному процесу окремі регістри, програмні лічильники, пам'ять купи та стека;
- процеси повністю незалежні і немає спільної пам'яті чи даних;
- перемикання контексту та зв'язок між процесами займають більше часу, оскільки вони важкі, створення нових процесів потребує більше ресурсів у порівнянні з потоками.
Потік – це легкий процес
- це одиниця виконання всередині цього процесу, один процес може мати один і більше потоків;
- кожен потік з цим процесом поділяє пам'ять та ресурси, тому програмісти мають справу з паралелізмом та синхронізацією;
- створення нового потоку потребує менше ресурсів, ніж новий процес, комунікація між потоками та перемиканням контексту швидше порівняно з перемиканням контексту та комунікації процесів.
Багатопотоковість та алгоритм поділу часу
Уявіть, що у вас є пристрій з одним ядром і запущена програма, яка вимагає k потоків ( k > 1 ). І тут одному процесору доводиться обробляти k потоків. Як процесор обробляє k – потоків? Ось де алгоритм time-slicing робить усю магію.
За наявності кількох потоків час обробки одноядерного процесора розподіляється між процесами та потоками.Всі потоки плануються процесором випадковим чином за допомогою thread scheduler і кожен потік отримує мінімальну кількість часу для виконання. Як тільки виділений час спливає, інший потік отримує свою частину процесорного часу та починає свою частину виконання. Цей процес виділення процесором часу потокам триває до того часу, поки всі потоки завершать своє виконання. Це називається алгоритмом квантування часу (time slicing algorithm). Під капотом потоки виконуються послідовно, проте вони виконуються настільки швидко, що виникає відчуття паралельного виконання. Це називається simulated або fake concurrency. .
Це було коротке введення у багатопоточність у Java . У наступних постах ми розглянемо життєвий цикл потоків, різницю між процесом і потоком, різницю між багатопоточністю та паралелізмом і т. д. Далі буде.
