Czy wywołanie metody zsynchronizowanej z innej metody synchronicznej jest bezpieczne?

81

Jeśli metoda synchroniczna wywołuje inną metodę zsynchronizowaną, czy jest bezpieczna wątkowo?

void synchronized method1() {
     method2()
}

void synchronized method2() {
}
user705414
źródło
Czy ten artykuł pomógłby Ci odpowiedzieć, czy też gdzie jesteś zdezorientowany? kalyanchakravarthy.net/?p=413
James Black
Tak - w rzeczywistości nie musisz oznaczać metody Method2 jako synchronizowanej, zakładając, że jest wywoływana tylko w kontekście podanym powyżej.
debracey
4
Ponadto, czy jest to wątkowe, będzie zależeć od tego, co dzieje się w obu metodach. Jeśli na przykład wywołują one listy bez ochrony wątków, mogą nie być wątkowo bezpieczne, jeśli inny wątek mógłby zmodyfikować tę kolekcję.
James Black,
Odpowiedzią na to, co przypuszczam, jest rzeczywiste pytanie: tak, słowo kluczowe synchronized używa rekurencyjnych blokad; możesz bezpiecznie wywołać metodę synchronizowaną z innej metody synchronicznej.
Brett Kail
Minęło trochę czasu, ale nadal jest to pierwszy hit w Google, więc: Tak, zsynchronizowane bloki / metody na tym samym obiekcie są ponownie wprowadzane. stackoverflow.com/questions/12219376/reentrant-synchronization
Szocske

Odpowiedzi:

103

Tak, kiedy oznaczysz metody jako synchronized, to naprawdę robisz to:

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

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

Kiedy wywołanie wątku dostaje się do metody method2 z metody method1, wtedy upewnia się, że utrzymuje blokadę, do thisktórej już będzie, a następnie może przejść.

Gdy wątek przechodzi bezpośrednio do metody method1 lub method2, będzie blokować, dopóki nie będzie mógł uzyskać funkcji lock ( this), a następnie wejdzie.

Jak zauważył James Black w komentarzach, musisz być świadomy tego, co robisz w treści metody.

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

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

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

Nagle nie jest to bezpieczne wątkowo, ponieważ patrzysz na a ConcurrentModificationExceptionw przyszłości, ponieważ method3jest niezsynchronizowany, a zatem może zostać wywołany przez wątek A, gdy wątek B pracuje method1.

pickypg
źródło
Próbuję odpowiedzieć na pytanie prawie identyczne z zadanym tutaj. To są 2 możliwe odpowiedzi (pozostałe 2 twierdzą, że nie będzie działać), która jest poprawna? C. Kod będzie działał, ale istnieje potencjalna sytuacja zakleszczenia D. Kod będzie działał poprawnie, ponieważ Java zapewnia synchronizację z powtórzeniem, umożliwiając wątkowi uzyskanie tej samej blokady więcej niż raz ----- Zgaduję, że to D, ale może potencjalna sytuacja impasu zależy od treści metody?
@ user3140993 Ten kod nie ma szans na zakleszczenie. method3pokazuje niebezpieczne operacje na wątkach, ale jesteś na bieżąco z synchronizacją ponownie.
pickypg
7

Jest metodą oznaczoną za pomocą zsynchronizowanego wywołania innego zsynchronizowanego wątku metody.

Ogólnie rzecz biorąc, nie można tego powiedzieć. Zależy to od tego, co robią metody i jakie są inne metody w tej samej i innych klasach.

Możemy jednak być pewni, że wywołania metody method1 i method2 na tym samym obiekcie przez różne wątki nie będą wykonywane jednocześnie. W zależności od tego, co robią metody, może to wystarczyć, aby stwierdzić, że klasa jest bezpieczna wątkowo w odniesieniu do tych metod.

Stephen C.
źródło
2

Z witryny samouczków Java http://download.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

  1. Nie ma możliwości przeplotu dwóch wywołań metod zsynchronizowanych na tym samym obiekcie. Gdy jeden wątek wykonuje zsynchronizowaną metodę dla obiektu, wszystkie inne wątki, które wywołują zsynchronizowane metody dla tego samego bloku obiektu (wstrzymują wykonywanie), dopóki pierwszy wątek nie zostanie zakończony z obiektem.

  2. gdy metoda synchroniczna kończy działanie, automatycznie ustanawia relację zdarzenie przed każdym kolejnym wywołaniem metody synchronicznej dla tego samego obiektu. Gwarantuje to, że zmiany stanu obiektu są widoczne dla wszystkich wątków

Tak więc Java zapewni, że jeśli 2 wątki wykonują tę samą metodę, metody nie będą wykonywane jednocześnie, ale jedna po drugiej.

Ale musisz zdawać sobie sprawę z problemu Liveness, http://download.oracle.com/javase/tutorial/essential/concurrency/starvelive.html

A także, czy blokujesz bez powodzenia, ponieważ w kodzie, którego użyłeś tego , który blokuje cały obiekt, jeśli twój obiekt potrzebuje dostępu synchronizacyjnego tylko do jednej zmiennej, powinieneś po prostu zablokować tę zmienną.

Stephen Lee
źródło
@Stephen Lee - nie możesz zablokować zmiennej. Następnie mówisz, synchronized (this.someVar)że patrzysz na obiekt, którego odniesienie jest zachowane someVar. To rozróżnienie jest bardzo ważne.
Stephen C