JavaRush /Java блог /Random UA /Рівень 27. Відповіді на запитання до співбесіди на тему р...
DefNeo
36 рівень

Рівень 27. Відповіді на запитання до співбесіди на тему рівня

Стаття з групи Random UA
Рівень 27. Відповіді на запитання до співбесіди на тему рівня - 1
  1. Що таке дідлок?

    Дідлок – це ситуація, коли два і більше ниток заблоковані, які чекають один на одного. Дідлоком також називається взаємне блокування.

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

    Бувають взаємні блокування системи синхронізації (вирішуються призначенням системи);

    Взаємне блокування між об'єктами (різні об'єкти намагаються отримати доступ до тих самих синхронізованих блоків);

    Ресурсне взаємоблокування (при спробі отримати доступ до деяких ресурсів, які можуть використовувати одночасно лише один потік).

  2. Які ви знаєте стратегії, що запобігають появі дідлок?

    Безперечно, якщо код написаний без будь-яких помилок, то взаємних блокувань у ньому не буде. Але хто може доручитись, що його код написаний без помилок? Безумовно, тестування допомагає виявити значну частину помилок, але, як ми вже бачабо раніше, помилки в багатопотоковому коді нелегко діагностувати і навіть після тестування не можна бути впевненим у відсутності ситуацій взаємних блокувань. Чи можемо ми перестрахуватися від блокувань? Відповідь – так. Подібні техніки використовуються в двигунах баз даних, яким часто потрібно відновлюватися після взаємних блокувань (пов'язаних з механізмом транзакцій в БД). Інтерфейс Lockта його реалізації доступні у пакеті java.util.concurrent.locksдозволяють спробувати зайняти монітор, пов'язаний з екземпляром даного класу методом tryLock(повертає true, якщо вдалося зайняти монітор).

    Також є стратегія застосування відкритих викликів, тобто викликати методи інших об'єктів поза синхронізованим блоком.

    Посилання на статтю: Взаємне блокування в Java і методи боротьби з нею

  3. Чи можуть виникнути дідлок при використанні методів wait-notify?

    Відповісти на це питання важко мені, але прочитавши в інтернеті різні дискусії на цю тему, можна сказати наступне:

    Дедлоков можна уникнути з допомогою розумного використання synchronized, volatile, монітора ( wait(), notify(), notifyAll()), і якщо копати глибше, використовуючи класи java.utils.concurrent: замість звичайних колекцій - многопоточные варіанти ( ConcurrentHashMap, наприклад); якщо необхідний складніший спосіб синхронізації потоків — різні CyclicBarrier, CountDownLatch.

    Якщо грамотно використовувати waitnotifyто дідлоки виникнути не повинні.)))

    Ось посилання: Взаємне блокування або Deadlock.

  4. Що найчастіше використовується: 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.

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

  5. Метод waitрекомендується використовувати з конструкціями ifчи while?

    Тут відповім просто цитатою із сайту: Синхронізація потоків

    З приводу виклику методу wait. Це вже із розряду чистої техніки. Рекомендується викликати waitзсередини циклу while. Тобто, писати не

    if (some condition){
        obj.wait()
    }

    ..., а

    while (some condition){
        obj.wait()
    }

    Для чого це треба. Справа в тому, що notifyможе викликати будь-хто. Просто помилково, від якої ніхто не застрахований. У тому випадку з досвіду, про який я розповідав вище, ми взялися за переробку саме для того, щоб уникнути такої можливості. Просто сховали об'єкт, на якому відбувається синхронізація. І доступ до нього мав лише наш код. Це хороша практика, але не завжди можливо, на жаль. Так от, якщо потік чекає на виконання деякої умови – варіант з whileнадійнішим. Якщо потік пустабо помилково – він знову перевірить умову і, якщо треба, чекатиме далі.

    Крім того, не виключена можливість простого виходу з очікування без виклику notify. Я чесно зізнаюся, що не бачив цього у специфікації віртуальної машини, хоч спеціально шукав. Але деякі гуру стверджують, що VM може вийти зі стану очікування мимоволі. Більше того, періодично це спостерігається. Якщо хтось дасть посилання на відповідну специфікацію – буду вдячний!

  6. Що відбувається після виклику методу notifyAll?

    The java.lang.Object.notifyAll() переміщаються всі threads, що є waiting on this object's monitor. Під час подачі на object 's monitor за допомогою одного з побіжних методів.

    Пробуджує всі нитки, які чекали на цьому моніторі.

  7. Які вигоди отримує об'єкт, якщо він 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, наприклад, встановить її глибоко в минуле. Наслідки можуть бути найдивовижнішими.

  8. Що таке "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), це означає, що вони побудовані таким чином, що кілька потоків можуть проходити через них в один час і ніколи не викликають проблем, це може бути у випадку, коли метод виконує лише один атомарний виклик. Атомарні виклики – це виклики, які не можуть бути перервані, і може бути реалізовані лише одним потоком.

  9. Що таке happens-before?

    Є стаття на вікіпедії, вона не конкретно про "happens-before", але все-таки.

    А так:
    «Виконується раніше» (англ. happens before) — відношення суворого часткового порядку (арефлексивне, антисиметричне, транзитивне), введене між атомарними командами ( ++і --не атомарними!), придумане Леслі Лемпортом і не означає «фізично раніше». Воно означає, що друга команда буде "в курсі" змін, проведених першою.

    Модель пам'яті Java

    Зокрема, одне виконується насамперед для таких операцій (список не вичерпний):

    • Синхронізація та монітори:
    • Захоплення монітора (початок 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()потоку та виявлення факту зупинки.
  10. Що таке 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).

  11. Який виняток вилетить, якщо викликати waitне в блоці synchronized?

    Ось посилання: Відповіді на запитання на співбесіду Multithreading (частина 2)

    Основна причина виклику waitі notifyзі статичного блоку чи методу у тому, що Java API обов'язково вимагає цього. Якщо ви викличете їх не з синхронізованого блоку, код викине IllegalMonitorStateException. Більш хитра причина в тому, щоб уникнути стану гонки між викликами waitта notify.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ