programowanieProgramista Backend

Wyjaśnij, czym jest safe publication w Javie, dlaczego jest potrzebna w programowaniu wielowątkowym oraz jakie są sposoby na jej zapewnienie.

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Safe Publication (bezpieczna publikacja) to gwarantowane bezpieczne przekazywanie obiektu między wątkami: jeśli jeden wątek tworzy i inicjalizuje obiekt, inny wątek zawsze zobaczy w pełni skonstruowany obiekt, a nie jego częściowo zainicjalizowany stan.

Bez safe publication można napotkać race condition: jeden wątek widzi nie-zainicjalizowaną część obiektu, nawet jeśli konstruktor już zakończył działanie.

Sposoby zapewnienia safe publication:

  • Używać pól final — ich wartość jest gwarantowana jako widoczna dla innych wątków po zakończeniu konstruktora.
  • Publikować obiekt za pomocą zmiennej volatile lub AtomicReference.
  • Publikować obiekt za pomocą bezpiecznych dla wątków kontenerów (np. kolekcje z java.util.concurrent).
  • Używać synchronized przy tworzeniu i odczycie obiektu.

Przykład (niebezpieczne):

public class Holder { private int n; public Holder(int n) { this.n = n; } } Holder holder; void publish() { holder = new Holder(42); } // Bezpieczeństwo nie jest gwarantowane

Mogą być widoczne nie w pełni skonstruowane obiekty!

Przykład (bezpieczne):

volatile Holder holder; void publish() { holder = new Holder(42); } // Odczyt holdera również będzie bezpieczny

Pytanie podchwytliwe

Czy jeśli wszystkie pola w obiekcie są final, to czy zawsze gwarantuje to safe publication?

Odpowiedź: Nie, tylko jeśli odniesienie do nowego obiektu zostanie opublikowane przed zakończeniem konstruktora. Jeśli odniesienie trafia do innych wątków przed zakończeniem konstruktora, pola mogą być nie w pełni zainicjalizowane. Należy unikać publikowania this lub odniesień do nie w pełni skonstruowanych obiektów podczas wykonywania konstruktora.

Przykład (niebezpieczne!):

public class PublishEscape { public static PublishEscape instance; public PublishEscape() { instance = this; // Źle! this jest publikowane przed zakończeniem konstruktora } }

Historia

Programista przypisywał odniesienie do instancji Service w polu dostępnym przez static, przed zakończeniem konstruktora. W rezultacie inny wątek otrzymał częściowo skonstruowany obiekt, co spowodowało nieprzewidywalne zachowanie w produkcji.


Historia

W aplikacji webowej obiekty singleton były tworzone bez dodatkowej synchronizacji. Pod obciążeniem część wątków otrzymywała null lub niepoprawnie zainicjalizowane pola, co prowadziło do Intermittent NullPointerException.


Historia

W bibliotece używano zwykłego List do cache między wątkami - brakowało safe publication, inicjalizacja nie gwarantowała widoczności dla nowych wątków. W rezultacie cache działał chaotycznie, naruszając integralność danych.