-
Що таке дідлок?
Дідлок – це ситуація, коли два і більше ниток заблоковані, які чекають один на одного. Дідлоком також називається взаємне блокування.
Взаємне блокування - це ситуація в якій, два або більше процесу займаючи деякі ресурси, намагаються отримати деякі інші ресурси, зайняті іншими процесами і жоден з процесів не може зайняти необхідний їм ресурс, і відповідно звільнити займаний.
Бувають взаємні блокування системи синхронізації (вирішуються призначенням системи);
Взаємне блокування між об'єктами (різні об'єкти намагаються отримати доступ до тих самих синхронізованих блоків);
Ресурсне взаємоблокування (при спробі отримати доступ до деяких ресурсів, які можуть використовувати одночасно лише один потік).
-
Які ви знаєте стратегії, що запобігають появі дідлок?
Безперечно, якщо код написаний без будь-яких помилок, то взаємних блокувань у ньому не буде. Але хто може доручитись, що його код написаний без помилок? Безумовно, тестування допомагає виявити значну частину помилок, але, як ми вже бачабо раніше, помилки в багатопотоковому коді нелегко діагностувати і навіть після тестування не можна бути впевненим у відсутності ситуацій взаємних блокувань. Чи можемо ми перестрахуватися від блокувань? Відповідь – так. Подібні техніки використовуються в двигунах баз даних, яким часто потрібно відновлюватися після взаємних блокувань (пов'язаних з механізмом транзакцій в БД). Інтерфейс
Lock
та його реалізації доступні у пакетіjava.util.concurrent.locks
дозволяють спробувати зайняти монітор, пов'язаний з екземпляром даного класу методомtryLock
(повертає true, якщо вдалося зайняти монітор).Також є стратегія застосування відкритих викликів, тобто викликати методи інших об'єктів поза синхронізованим блоком.
Посилання на статтю: Взаємне блокування в Java і методи боротьби з нею
-
Чи можуть виникнути дідлок при використанні методів
wait-notify
?Відповісти на це питання важко мені, але прочитавши в інтернеті різні дискусії на цю тему, можна сказати наступне:
Дедлоков можна уникнути з допомогою розумного використання
synchronized
,volatile
, монітора (wait()
,notify()
,notifyAll()
), і якщо копати глибше, використовуючи класиjava.utils.concurrent
: замість звичайних колекцій - многопоточные варіанти (ConcurrentHashMap
, наприклад); якщо необхідний складніший спосіб синхронізації потоків — різніCyclicBarrier
,CountDownLatch
.Якщо грамотно використовувати
wait
–notify
то дідлоки виникнути не повинні.)))Ось посилання: Взаємне блокування або Deadlock.
-
Що найчастіше використовується:
notify
абоnotifyAll
?java.lang.Object.notify() wakes up a single thread that is waiting on this object's monitor. Якщо кожний threads waiting on this object, один з них є chosen to be awakened. choice є arbitrary and occurs at discretion of the implementation. Під час подачі на object 's monitor за допомогою одного з побіжних методів.
Цей метод повинен тільки бути названий під час того, що це власник цього об'єкта monitor. Під час переміщення власника object's monitor в одному з трьох способів:
- Використовуючи synchronized instance метод того, що об'єкт.
- Використовуючи body of synchronized statement that synchronizes on the object.
- Для об'єктів типу класу виконується synchronized static method that class.
Тільки один день в часі може бути власним об'єктом monitor.
The java.lang.Object.notifyAll() переміщаються всі threads, що є waiting on this object's monitor. Під час подачі на object 's monitor за допомогою одного з побіжних методів.
Нахилені threads не можуть бути виконані until the current thread relinquishes the lock on this object. Звільнені літери будуть складені в будь-якому manner with any other threads that might be actively competing to synchronize on this object; для прикладу, звільнені ходи приємно не надійні привілеї або розрив в тому, що next thread to lock this object.
Цей метод повинен тільки бути названий під час того, що це власник цього об'єкта monitor.
Це уривки із документації. Питання – то здебільшого риторичне, дивлячись якесь додаток, залежно від ситуації))) Я навіть не знаю, як би я відповів. Якщо у когось є якісь здогадки, то прошу в коментарях залишити, дуже радий почитати.
-
Метод
wait
рекомендується використовувати з конструкціямиif
чиwhile
?Тут відповім просто цитатою із сайту: Синхронізація потоків
З приводу виклику методу
wait
. Це вже із розряду чистої техніки. Рекомендується викликатиwait
зсередини циклуwhile
. Тобто, писати неif (some condition){ obj.wait() }
..., а
while (some condition){ obj.wait() }
Для чого це треба. Справа в тому, що
notify
може викликати будь-хто. Просто помилково, від якої ніхто не застрахований. У тому випадку з досвіду, про який я розповідав вище, ми взялися за переробку саме для того, щоб уникнути такої можливості. Просто сховали об'єкт, на якому відбувається синхронізація. І доступ до нього мав лише наш код. Це хороша практика, але не завжди можливо, на жаль. Так от, якщо потік чекає на виконання деякої умови – варіант зwhile
надійнішим. Якщо потік пустабо помилково – він знову перевірить умову і, якщо треба, чекатиме далі.Крім того, не виключена можливість простого виходу з очікування без виклику
notify
. Я чесно зізнаюся, що не бачив цього у специфікації віртуальної машини, хоч спеціально шукав. Але деякі гуру стверджують, що VM може вийти зі стану очікування мимоволі. Більше того, періодично це спостерігається. Якщо хтось дасть посилання на відповідну специфікацію – буду вдячний! -
Що відбувається після виклику методу
notifyAll
?The java.lang.Object.notifyAll() переміщаються всі threads, що є waiting on this object's monitor. Під час подачі на object 's monitor за допомогою одного з побіжних методів.
Пробуджує всі нитки, які чекали на цьому моніторі.
-
Які вигоди отримує об'єкт, якщо він
immutable
?Знайшов коментар на: immutable-об'єкти та багатопоточність
Immutable
об'єкт — це об'єкт, стан якого після створення неможливо змінити. У випадку Java це означає, що всі поля екземпляра у клас відзначені якfinal
і є примітивами або тежimmutable
типами.Приклад:
public class ImmutablePoint { private final int x; private final int y; private final String description; public ImmutablePoint(int x, int y, String description) { this.x = x; this.y = y; this.description = description; } }
Після створення екземпляра
ImmutablePoint
його модифікація неможлива.Найпростіший приклад
immutable
класу з JDK цеString
. Будь-які методи, які ви викликаєте на рядку (наприкладdescription.toLowerCase()
), повернуть новий рядок, а не модифікують вихідний.Приклад
mutable
класу з JDK -Date
. НаприкладmyDate.setHours(x)
модифікує сам екземплярmyDate
!Є різниця між
immutable
-об'єктом (тобто незмінним), іfinal
-посиланням.Ключове слово
final
для об'єктних типів гарантує незмінність лише посилання, але з самого об'єкта. Наприклад, якщо у вас єfinal
посилання наArrayList<T>
, ви можете додавати до нього нові елементи або змінювати існуючі.У разі
immutable
об'єкта об'єкт після закінчення конструктора не змінюється взагалі. Лише модифікатораfinal
для цього недостатньо, необхідно, щоб усі подоб'єкти були теж незмінними. Ви в принципі можете тримати всередині посилання на об'єкт, що змінюється, але звертатися з ним так, щоб він не змінювався.Використання незмінних об'єктів дає багато вигод. Наприклад, про такий об'єкт набагато легше судити в ситуації, коли в багатьох частинах програми є посилання на нього (для об'єкта, що змінюється, будь-яка частина програми може викликати мутуючу функцію в практично будь-який момент часу і з будь-якого потоку).
Але те, що для нас є важливим у контексті питання — незмінні об'єкти не вимагають синхронізації при багатопоточному доступі. Ось, власне, і вся рекомендація: використовуйте незмінні об'єкти, і вам не доведеться думати про те, що потрібно, а що не потрібно синхронізувати. Єдина можлива проблема — якщо ви всередині конструктора, що ще не відпрацював, публікуєте посилання на об'єкт, через яке до нього може отримати доступ хтось ще, і побачити об'єкт у стані, що змінюється! (Це буває не так і рідко. Наприклад, іноді програміст хоче додати об'єкт у конструкторі в колекцію всіх об'єктів даного типу.)
Слід розрізняти дійсно незмінні об'єкти і об'єкти, що мають лише інтерфейс «тільки для читання». При читанні об'єкт може змінювати свою внутрішню структуру (наприклад, кешувати найсвіжіший запит даних). Такі об'єкти не є в строгому сенсі незмінними, і не можуть бути використані з різних потоків без обережності. (Тому, якщо ваш об'єкт включає інші об'єкти, переконайтеся, що документація гарантує їх незмінність!)
Зверніть увагу, що для полів постійного об'єкта ви фактично повинні використовувати
final
! Справа в так званій безпечній публікації. Дивіться. Інструкції в Java-програмі може бути переставлені як оптимізатором, і процесором (у Java досить слабка модель пам'яті). Тому, якщо не робити спеціальних дій, закінчення роботи конструктора та присвоєння значень полям може бути переставлено (але невидимо у рамках поточного потоку)! Використанняfinal
гарантує, що такого не станеться.У разі багатопотокового програмування переваги
immutable
класів очевидні: після створення об'єкти можна передавати іншим потокам і вони завжди будуть у актуальному стані. Тобто. вам не треба перевіряти чи не застарів стан вашого екземпляра і чи не модифікував його інший потік поки ви з ним працюєте. Наприклад, у вас є методbill(Date endDate)
, у ньому ви наївно перевіряєте відповідністьendDate
якимось попереднім умовам і починаєте з нею працювати. У цей момент інший потік може змінитиendDate
, наприклад, встановить її глибоко в минуле. Наслідки можуть бути найдивовижнішими. -
Що таке "thread-safe"?
Знову ж таки: What is thread Safe in java? [duplicate]
Thread safe means те, що метод або class instance може бути використана з multiple threads at the same time without any problems occuring.
Стан потоко-безпеки передбачає, що метод чи клас може бути використаний безліччю ниток без проблем зіткнення, тобто дідлок.
Consider the following method:
private int myInt = 0; public int AddOne() { int tmp = myInt; tmp = tmp + 1; myInt = tmp; return tmp; }
Now thread A and thread B both would like to execute AddOne(). but A starts first and reads the value of myInt (0) в tmp. Для того, щоб зазначити, що освітянин вирішить, щоб статевий кінець A і запобігти виконанню до дії B. Третій B зараз також витрачає ціну myInt (завжди 0) в її власну варіаційну tmp. Підпис B вказує на введення методу, так в кінці myInt = 1. І 1 є відновленим. Now it's Thread A's turn again. Thread A continues. And adds 1 to tmp (tmp був 0 for thread A). And then saves this value in myInt. myInt is again 1.
Тут і нитку А і нитку B хочуть виконати
AddOne ()
. але А починається першою і зчитує значення myInt(0) у TMP. Тепер з деяких причин планувальник вирішує зупинити потік А та відкласти виконання нитки B. Потік В даний час також зчитує значення myInt (0) у його власній змінній TMP. Нитка B завершує весь метод так, що, зрештою, myInt = 1. І 1 повертається. Потік А продовжується. І додає 1 TMP (TMP 0 для нитки A). А потім зберігає це значення в myInt. myInt знову 1.Якщо в цьому випадку метод AddOne була названа в два рази, але тому, що метод не був впроваджений в третій надійний спосіб значення миттєво не є 2, as expected, але 1 згодом згодом read read the variable myInt befor updating it.
Так що в цьому випадку метод
AddOne
був викликаний двічі, але так як цей метод не був реалізований у потоці безпечним способом величина myInt не 2, як очікувалося, а 1, тому що другий потік читання змінної закінчивсяmyInt
перед першою ниткою до його оновлення.Creating thread safe methods is very hard in no trivial cases. І вони існують кілька технологій. У Java ви можете оцінити метод, як synchronized, це рішення, що тільки один кінець може виконувати те, що метод при виконанні часу. Інші threads wait in line. Це робить метод методу захисту, але якщо є багато праць для того, щоб зробити в спосіб, то це було багато часу. Ще одна технологія є 'марком тільки малої частини методу synchronized', створюючи lock або semaphore, і locking ця маленька частина (зазвичай називається critical section). Там є деякі методи, які є здійснені як lockless thread safe, які рішення, що вони будуть побудовані в такій мірі, що низка подій може йти через те, що в той же час без будь-яких проблем, які можуть бути atomic call.
Створення потоково-безпечних методів дуже важко. У Java можна позначити метод як синхронізований, це означатиме, що тільки один потік може виконати цей метод в даний момент часу. Інші нитки чекатимуть у черзі. Це робить метод потоково-безпечним, але якщо багато роботи доведеться зробити в методі, то на це буде витрачати багато часу. Інший метод полягає в розмітці лише малої частини методу, як синхронізованого шляхом створення локів (locks) або семафорів, і замикання цієї невеликої частини (звичайно званий критичний розділ (critical section)). Є навіть деякі методи, які реалізуються як беззамкові потокобезпечні (lockless thread safe), це означає, що вони побудовані таким чином, що кілька потоків можуть проходити через них в один час і ніколи не викликають проблем, це може бути у випадку, коли метод виконує лише один атомарний виклик. Атомарні виклики – це виклики, які не можуть бути перервані, і може бути реалізовані лише одним потоком.
-
Що таке happens-before?
Є стаття на вікіпедії, вона не конкретно про "happens-before", але все-таки.
А так:
«Виконується раніше» (англ. happens before) — відношення суворого часткового порядку (арефлексивне, антисиметричне, транзитивне), введене між атомарними командами (++
і--
не атомарними!), придумане Леслі Лемпортом і не означає «фізично раніше». Воно означає, що друга команда буде "в курсі" змін, проведених першою.Зокрема, одне виконується насамперед для таких операцій (список не вичерпний):
- Синхронізація та монітори:
- Захоплення монітора (початок
synchronized
, методlock
) і все, що після нього в тому ж потоці. - Повернення монітора (кінець
synchronized
, методunlock
) і все, що перед ним у тому ж потоці. - Таким чином, оптимізатор може заносити рядки до синхроблок, але не назовні.
- Повернення монітора та подальше захоплення іншим потоком.
- Запис та читання:
- Будь-які залежності за даними (тобто запис у будь-яку змінну і подальше читання її) в одному потоці.
- Все, що в тому ж потоці перед записом в
volatile
змінну, і сам запис. volatile
-Читання і все, що після нього в тому ж потоці.- Запис в
volatile
-змінну і наступне зчитування її.[4][2] Отже,volatile
-запис робить із пам'яттю те саме, що повернення монітора, а читання — те саме, що захоплення.[5] А значить: якщо один потік записав уvolatile
змінну, а другий виявив це, все, що передує запису, виконується насамперед, що йде після читання; див. ілюстрацію. - Для об'єктних змінних (наприклад,
volatile List x;
) такі сильні гарантії виконуються для посилання на об'єкт, але не для його вмісту. - Обслуговування об'єкту:
- Статична ініціалізація та будь-які дії з будь-якими екземплярами об'єктів.
- Запис
final
-поля в конструкторе[6] і що після конструктора. Як виняток із загальної транзитивності, це співвідношення happens-before не з'єднується транзитивно коїться з іншими правилами і тому може викликати межпоточную гонку.[7] - Будь-яка робота з об'єктом та
finalize()
. - Обслуговування потоку:
- Запуск потоку та будь-який код у потоці.
- Занулення змінних, що відносяться до потоку, та будь-який код у потоці.
- Код у потоці та
join();
код у потоці таisAlive() == false
. interrupt()
потоку та виявлення факту зупинки.
-
Що таке JMM?
Java Memory Model
Ось посилання: Chapter 17. Threads and Locks
І ось витяг з неї:
На пам'ятній моделі описано, ведеться програма і execution trace of that program, за допомогою якого execution trace є legal execution of program. Java програмування англійською мовою моделлю робіт вивчають їх read in execution trace a checking that the write observed by read is valid according to certain rules.
Я зрозумів на своєму рівні, що це набір правил:
Правило № 1: однопотокові програми виконуються псевдопослідовно. Це означає: насправді процесор може виконувати кілька операцій за такт, заодно змінивши їхній порядок, проте всі залежності за даними залишаються, так що поведінка не відрізняється від послідовної. Правило № 2: немає значень, що казна-звідки взялися. Читання будь-якої змінної (крім не-volatile long і double, для яких це правило може не виконуватися) видасть або значення за умовчанням (нуль), або щось, записане туди іншою командою. І правило № 3: інші події виконуються по порядку, якщо пов'язані ставленням суворого часткового порядку «виконується насамперед» (англ. happens before).
-
Який виняток вилетить, якщо викликати
wait
не в блоціsynchronized
?Ось посилання: Відповіді на запитання на співбесіду Multithreading (частина 2)
Основна причина виклику
wait
іnotify
зі статичного блоку чи методу у тому, що Java API обов'язково вимагає цього. Якщо ви викличете їх не з синхронізованого блоку, код викинеIllegalMonitorStateException
. Більш хитра причина в тому, щоб уникнути стану гонки між викликамиwait
таnotify
.
DefNeo
36 рівень
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ