Чи безпечно викликати синхронізований метод з іншого синхронізованого методу?


81

Якщо синхронізований метод викликає інший синхронізований метод, чи безпечний він для потоку?

void synchronized method1() {
     method2()
}

void synchronized method2() {
}

Чи допоможе ця стаття відповісти, чи де ви збентежені? kalyanchakravarthy.net/?p=413
Джеймс Блек

Так - насправді вам не потрібно позначати метод2 як синхронізований, припускаючи, що він викликається лише у контексті, наведеному вище.
debracey

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

Відповіддю на те, що я здогадуюсь, є власне питання: так, синхронізоване ключове слово використовує рекурсивні блокування; Ви можете безпечно викликати синхронізований метод з іншого синхронізованого методу.
Brett Kail

Минув деякий час, але це все ще перше потрапляння в Google, тому: Так, синхронізовані блоки / методи на тому самому об’єкті повертаються. stackoverflow.com/questions/12219376/reentrant-synchronization
Szocske

Відповіді:


103

Так, коли ви позначаєте методи як synchronized, тоді ви дійсно робите це:

void method1() {
    synchronized (this) {
        method2()
    }
}

void method2() {
    synchronized (this) {
    }
}

Коли виклик потоку потрапляє в method2 з method1, тоді він гарантує, що він утримує замок this, що вже буде, і тоді він може пройти.

Коли потік потрапляє безпосередньо в method1 або method2, тоді він буде блокувати, поки не зможе отримати замок ( this), а потім увійде.

Як зазначив Джеймс Блек у коментарях, ви повинні бути в курсі того, що ви робите всередині тіла методу.

private final List<T> data = new ArrayList<T>();

public synchronized void method1() {
    for (T item : data) {
        // ..
    }
}

public void method3() {
    data.clear();
}

Раптом це не безпечно для потоку, оскільки ви дивитесь на ConcurrentModificationExceptionсвоє майбутнє, оскільки method3воно несинхронізоване, і, отже, може бути викликане Thread A, поки Thread B працює method1.


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

@ user3140993 Код тут не має шансів на глухий кут. method3показує небезпечні операції з потоковими потоками, але ви знаєте про повторну синхронізацію.
pickypg

7

Чи є метод, позначений синхронізованим викликом, іншим безпечним потоком синхронізованого методу.

Загалом, сказати не можна. Це залежить від того, що роблять методи, і від того, які інші методи в тому ж та інших класах.

Однак ми можемо бути впевнені, що виклики method1 і method2 для одного і того ж об'єкта, зроблені різними потоками, не будуть виконуватися одночасно. Залежно від того, що роблять методи, цього може бути достатньо, щоб сказати, що клас є безпечним для потоків щодо цих методів.


2

З веб-сайту підручників Java http://download.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

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

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

Отже, Java забезпечить, що якщо 2 потоки виконують один і той же метод, методи виконуватимуться не одночасно, а один за одним.

Але ви повинні знати про проблему Liveness, http://download.oracle.com/javase/tutorial/essential/concurrency/starvelive.html

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


@Stephen Lee - ви не можете заблокувати змінну. Потім ви кажете synchronized (this.someVar), що дивитесь на об’єкт, посилання на який зберігається someVar. Розрізнення дуже важливо.
Stephen C
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.