programowanieProgramista Backend

Opisz, jak działa blok synchronized w Javie. Jakie są jego cechy, jak wybrać obiekt do synchronizacji i jak niewłaściwy wybór może prowadzić do błędów?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

synchronized — słowo kluczowe, które umożliwia bezpieczny dostęp do krytycznych sekcji kodu w kontekście wielowątkowym. Może być używane zarówno dla metody (np. public synchronized void foo()), jak i dla bloku kodu (synchronized(obj) { ... }). Gdy wątek wchodzi do bloku synchronized, uzyskuje monitor (lock) obiektu. Dopóki monitor jest zajęty, inne wątki nie mogą wejść do innego bloku synchronized używającego tego samego obiektu jako monitora.

Cechy:

  • Synchronizacja na tym samym obiekcie zapewnia wzajemne wykluczanie (tylko jeden wątek może wykonywać blok w danym momencie).
  • Można synchronizować na this, na obiekcie statycznym (np. na klasie), na dowolnym obiekcie.
  • Jeśli jako monitor wybierze się obiekt dostępny dla wielu wątków lub, przeciwnie, zbyt lokalny obiekt, można naruszyć bezpieczeństwo wątków.

Przykład wyboru obiektu synchronizacji

public class Counter { private int count; private final Object lock = new Object(); // prywatny obiekt public void increment() { synchronized(lock) { count++; } } }

Dlaczego lepiej używać prywatnego locka? Ponieważ jeśli synchronizować na publicznym obiekcie (np. na this lub na ogólnodostępnym stringu), zewnętrzny kod również może przejąć ten monitor, co prowadzi do zakleszczeń lub nieprawidłowego działania.

Pytanie podchwytliwe.

Pytanie: Co się stanie, jeśli synchronizować na obiekcie typu String, zawierającym stałą wartość?

Odpowiedź: Łańcuchy w Javie są internowane (te same obiekty dla tych samych literałów). Jeśli synchronizować na łańcuchu w postaci synchronized("lock"), można przypadkowo natknąć się na inny kod, który synchronizuje na tym samym literale, co prowadzi do nieoczekiwanej blokady między zupełnie różnymi częściami programu.

Przykład (nie wolno tak robić):

synchronized("LOCK") { ... }

Przykłady rzeczywistych błędów z powodu braku znajomości niuansów tematu.


Historia

W wielowątkowym systemie handlowym do synchronizacji używano publicznych obiektów, a zewnętrzny kod mógł przejąć lock, co doprowadziło do czasowych zakleszczeń między wątkami różnych modułów i przestojów na giełdzie.


Historia

Młody programista synchronizował dostęp do kolekcji na podstawie literału stringowego. Inna część kodu również synchronizowała się na łańcuchu o tej samej wartości. Te wątki ustawiały się w kolejce za sobą, co spowodowało wyraźne spowolnienie logiki biznesowej.


Historia

Do synchronizacji za każdym razem wybierano nowy obiekt: synchronized(new Object()) { ... }. W rezultacie synchronizacja w ogóle nie działała, a do danych miał jednoczesny dostęp różne wątki. Zostało to wykryte dopiero podczas testów obciążeniowych.