Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ППН ТП.docx
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
98.74 Кб
Скачать

13.3. Багатопоточність і синхронізація

Java, мабуть, є єдиною універсальною мовою програмування, в якому механізми створення ниток підтримуються вбудованими засобами мови. У традиційних мовах програмування (наприклад, C) створення ниток забезпечується системно-залежними бібліотеками, що забезпечують API ОС. У Java засоби створення ниток системно-незалежні.

В Java-програмі нитка представляє окремий клас, який може бути створений

• або як підклас (спадкоємець) суперкласу протектора;

• або як клас, що реалізовує інтерфейс Runnable, всередині цього класу повинна бути змінна екземпляра класу - посилання на об'єкт класу протектора.

Суперклас Tread протектора і інтерфейс Runnable визначені в базовій бібліотеці мови Java - пакеті java.lang. При будь-якому варіанті створення в класі-нитки повинен бути реалізований метод Run (). Виконання методу Start () для примірника такого класу викликає виконання методу Run () в окремому потоці обчислення.

Як ми побачили в попередньому розділі, Java VM забезпечує для кожної нитки власне середовище обчислення - власний набір регістрів і стек (в деяких реалізаціях Java VM забезпечує для нитки також і власну купу).

Але Java VM не виконує дії з планування ниток на виконання. Для цього бібліотечні методи Java звертаються до ОС, використовуючи API тієї ОС, в середовищі якої працює Java VM.

Для ниток в Java передбачено управління пріоритетами (методи getPriority (), SetPriority ()), однак, і тут Java використовує механізми управління пріоритетами ОС. Так, уже класичним для підручників з Java є приклад аплету, в якому по екрану "наввипередки" рухаються кілька об'єктів, рух кожного здійснюється в окремій нитки і з власним пріоритетом. Цей приклад дуже наочно демонструється в середовищах, наприклад, OS / 2 та Linux, але виглядає не дуже переконливим у середовищі Windows 95/98, так як пріоритет нитки не надто впливає на швидкість руху об'єкту - приблизно так, як показано на малюнку 13.5.

Будь-яка нитка може бути зроблена ниткою, "демоном". Нитка-"демон" продовжує виконуватися навіть після закінчення тієї нитки, в якій вона була створена. Нитка стає "демоном" при її створенні тільки в тому випадку, якщо вона створюється з нитки-"демона". Програма може змінити стану нитки за допомогою методу setDaemon () класу Thread. Виконання будь-якої програми Java VM починає з єдиною нитки (в якій викликається метод основного ()), і ця нитка запускається не як "демон". Java VM продовжує існувати, поки не завершаться всі нитки не-"демони".

У мові Java є також клас ThreadGroup - група ниток, яка може управлятися спільно.

Якщо в Java передбачені нитки, то, природньо, повинні бути передбачені і засоби синхронізації і взаємного виключення при паралельній роботі ниток. Основним засобом синхронізації і взаємного виключення в Java є ключове слово синхронізовані, яке може вживатися перед яким програмним блоком. Ключове слово синхронізовані визначає неможливість використання програмного блоку двома або більше ниток одночасно. У Java синхронізований-блоки називаються моніторами і їх фактична тотожність моніторам Хоара, описаним в розділі 8,8 частини I, очевидна. Залежно від деталей способу вживання синхронізовані може працювати як:

• захищена (охоронець - 8,8 см розділ частини I). Процедура - в тому випадку, якщо синхронізовані-блок являє собою цілий метод, однак, на відміну від описаних нами охоронець-процедур одночасне входження в різні синхронізований-методи можливо;

• анонімні дужки критичної секції - в тому випадку, якщо синхронізовані-блок є просто програмним блоком;

• дужки критичної секції із захистом обраного ресурсу - в тому випадку, якщо після ключового слова синхронізовані вказується в дужках посилання на об'єкт.

У початковій версії мови Java для класу Тема передбачені методи:

• резюме () - призупинити виконання нитки;

• призупинити () - відновити виконання нитки;

• yeld () - зробити паузу у виконанні нитки, щоб дати можливість виконатися інший нитки;

• приєднатися () - очікувати завершення нитки.

Ці засоби дозволяють синхронізувати роботу ниток, але в наступних версіях був (поряд зі старими засобами) введено новий, більш стрункий апарат синхронізації і взаємного виключення.

Клас об'єкта має три методи:

• чекати () - очікувати повідомлення про це об'єкті;

• повідомити () - послати повідомлення однієї з ниток, що чекають повідомлення про це об'єкті;

• notifyAll () - послати повідомлення всім з ниткам, ждущим повідомлення про це об'єкті.

Оскільки клас об'єкта є коренем ієрархії класів, об'єкти всіх - стандартних і користувальницьких - класів є його підкласами і успадковують ці методи. Ці методи аналогічні операціям і чекати сигналу, описаним нами в розділі 8,7 частини I. Реалізація, наприклад, загального (з можливим значенням, більшим 1) семафора з використанням цих коштів буде виглядати наступним чином:

   / / ** Семафор реалізується у вигляді класу Semaphore

   Семафор суспільного класу

   {

    / / Значення семафора

    приватних Int Semaphore_value;

    / / ** Порожній конструктор семафора, за замовчуванням початкове значення семафора - 0

    громадських Semaphore ()

    {

     це (0);

    }

    / / ** Конструктор з параметром - початковим значенням семафора,

    / / Якщо задано від'ємне значення, встановлюється початкове значення 0

    громадських Semaphore (INT Вал)

    {

     якщо (значення <0) = 0 Semaphore_value

     ще Semaphore_value = значення;

    }

    / / ** V-операція. V-й P-операції оголошені синхронізовані,

    / / Щоб виключити одночасне виконання їх двома або більше нитками

    громадського синхронізовані недійсними V ()

    {

     / / Можливо пробуджує нитка, яка очікує у семафора

     якщо (Semaphore_value == 0) this.notify ();

     / / Збільшує значення семафора

     Semaphore_value + +;

    }

    / / ** P-операція

    громадського синхронізовані недійсними P () кидає InterruptedException

     / / Виключення може викидатися в очікування

    {

     / / Якщо значення семафора дорівнює 0, блокує нитку

     в той час як (лічильник == 0) this.wait ();

     / / Зменшує значення семафора

     Semaphore_value -;

    }

   }

У Java VM з кожним об'єктом зв'язується замок (замок) і список очікування (очікування безліч).

У специфікаціях байт-коду Java є спеціальні команди monitorenter і monitorexit, що встановлюють і знімають замок. Java VM, входячи в синхронізованих-блок, намагається виконати операцію установки замку і не продовжує виконання нитки, поки операція не буде виконана. При виході з синхронізованих-блоку виконується операція зняття замка.

Список очікування використовується методами чекати (), повідомляє (), notifyAll (). Він являє собою список ниток, які очікують повідомлення про даному об'єкті. Названі операції працюють з цим списком очевидним чином.

Слід зазначити, що багатопоточність, закладена в мові Java, має великі перспективи. Спочатку сама ідея ниток (а її перша комерційна реалізація була зроблена саме фірмою Sun Microsystems) виникла як рішення, покликане забезпечити ефективну завантаження устаткування в багатопроцесорних системах, але тепер властивості багатопоточності, які закладаються в програмне забезпечення, надають (поряд з іншими факторами) зворотний вплив на процесорні архітектури, стимулюючи розвиток в них засобів розпаралелювання обчислювального процесу.

Так, новий проект комп'ютерної архітектури фірми Sun Microsystems носить назву MAJC (мікропроцесорна архітектура для Java Computing - архітектура мікропроцесора для Java-обчислень), яке говорить саме за себе. У концепцію цієї архітектури (як, втім, і ряду інших нових архітектур) входить багатопроцесорна обробка з декількома "потоковими пристроями" в кожному процесорі. Така архітектура покликана забезпечити більш ефективну обробку на мережному сервері сучасних потоків даних, які характеризуються зростанням питомої ваги в них мультимедійної інформації.

Для отримання кращої продуктивності, крім багатопоточних мікропроцесорів, розробники MAJC застосовують технологію, звану "просторово-часовими обчисленнями" або "ймовірної багатопоточності". У цій технології прикладний програміст взагалі не буде турбуватися про розпаралелювання своїх програм, тому що ця робота буде зроблена за нього Java VM.

Сенс просторово-часових обчислень зводиться до наступного. Java VM перевіряє програму і припускає, що два методи можуть виконуватися одночасно на двох процесорах. Вона посилає метод на перший процесор, а метод B - на другий для можливого обчислення. Оскільки B - гаданий метод, він виконується в окремому адресному просторі, званому ймовірної пам'яттю. Якщо все йде добре, і ніякі залежності в даних не порушені, то приблизна пам'ять зливається з основною пам'яттю і програма обробляє наступну пару методів. Якщо відбулося порушення, то другий метод скасовується і гадана пам'ять "викидається".

Ідея просторово-часових обчислень не є кардинально новою, але вона не застосовувалася в звичайних багатопроцесорних системах без багатопоточності, оскільки дозволяла збільшити ефективність виконання програм в 2-процесорної конфігурації не більше, ніж на 5%. Розробники MAJC розраховують, що в їх архітектурі продуктивність послідовних додатків на двох процесорах повинна зрости в 1,6 рази.