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

61.Синхронізація роботи потоків.

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

4.1 . Зберігання змінних в пам'яті

Віртуальна машина підтримує основне сховище даних (основний зберігання) , в якому зберігаються значення всіх змінних і яке використовується всіма потоками . під змінними тут розуміються поля об'єктів і класів , а також елементи масивів . Що стосується локальних змінних і параметрів методів , їх значення не можуть бути доступним іншим потокам , тому вони не представляють інтересу . Для кожного потоку створюється його власна робоча пам'ять ( оперативна пам'ять) , в яку копіюються значення всіх змінних перед використанням. Розглянемо основні операції, доступні для потоків при роботі з пам'яттю : • use - читання значення змінної з робочої пам'яті потоку • assign - запис значення змінної в робочу пам'ять потоку • read - отримання значення змінної з основного сховища • load - збереження значення змінної , прочитаного з основного сховища , в робочій пам'яті • store - передача значення змінної з робочої пам'яті в основне сховище для майбутнього збереження • write - зберігає в основному сховище значення змінної , переданої командою store Підкреслимо , що перераховані команди не є методами будь-яких класів , вони не доступні програмісту . Сама віртуальна машина використовує їх для забезпечення коректної роботи потоків виконання. Потік , працюючи з змінної , регулярно застосовує команди використання призначити і для використання її існуючого значення і присвоєння нового. Крім цього , повинні здійснюватися дії по передачі значень із / в основне сховище . Вони виконуються в два етапи.

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

достатній простір для різних технологій оптимізацій (регістри , черги ,

кеш і т.д.). Послідовність команд підпорядковується таким правилам :

• всі дії, що виконуються одним потоком строго впорядковані , тобто виконуються одно

за іншим

• всі дії , що виконуються з однієї змінної в основному сховище пам'яті , строго

впорядковані , тобто слідують одне за іншим

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

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

4.2 . модифікатор volatile

При оголошенні полів об'єктів і класів може бути вказаний модифікатор volatile . Він встановлює більш суворі правила роботи зі значеннями змінних. Якщо потік збирається виконати команду use для volatile змінної , то потрібно, щоб попереднім дією над цієї змінної було обов'язково load , і на оборот - операція load може виконуватися тільки перед use . Таким чином , змінна і головне сховище завжди мають саме останнє значення цієї змінної . Аналогічно , якщо потік збирається виконати команду store для volatile змінної , то потрібно, щоб попереднім дією над цієї змінної було обов'язково assign , і навпаки - операція assign може виконуватися тільки якщо наступною буде store . Таким чином , змінна і головне сховище завжди мають саме останнє значення цієї змінної . Нарешті , якщо проводяться операції над декількома volatile змінними , то передача відповідних змін до основного сховища повинно проводиться строго в тому ж порядку. При роботі із звичайними змінними компілятор має більше простору для маневру. Наприклад , за сприятливих обставин може виявитися можливим передбачити значення змінної , заздалегідь обчислити і зберегти його , а потім у потрібний момент використовувати вже готовим. Потрібно звернути увагу на два 64 - розрядних типу double і long . Оскільки багато платформи підтримують лише 32 -бітну пам'ять , величини цих типів розглядаються як дві змінні , і всі описані дії робляться незалежно для двох половинок таких значень . Звичайно , якщо виробник віртуальної машини вважає за можливе , він може забезпечити атомарность операцій і над цими типами . Для volatile змінних це є обов'язковою вимогою .

4.3 . блокування

В основному сховище для кожного об'єкта підтримується блокування ( lock ) , над якою можна провести дві дії - встановити ( lock ) і зняти ( unlock ) . Тільки один потік в один момент часу може встановити блокування на деякий об'єкт . Якщо до того , як цей потік виконає операцію unlock , інший потік спробує встановити блокування , його виконання буде призупинено доти , поки перший потік не відпустить її . Операції lock і unlock накладають жорстке обмеження на роботу з змінними в робочій пам'яті потоку . Після успішно виконаного lock , робоча пам'ять очищається , і всі змінні необхідно заново зчитувати з основного сховища. Аналогічно , перед операцією unlock необхідно всі змінні зберегти в основному сховищі. Важливо підкреслити , що блокування є чимось на зразок прапора. Якщо блокування на об'єкт встановлена ​​, це не означає , що цим об'єктом не можна користуватися , що його

поля і методи стають недоступними - це не так. Єдина дія , яке стає неможливим - встановити цю ж блокування іншому потоку до тих пір, поки перший потік не виконає unlock . У Java -програмі для того , щоб скористатися механізмом блокувань , існує ключове слово synchronized . Воно може бути застосоване у двох варіантах - для оголошення synchronized - блоку і як модифікатор методу . В обох випадках дія його

приблизно однакове.

Synchronized - блок записується таким чином :

synchronized ( ref ) {

...

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]