Korzystanie z jednej mapy na wielu wątkach

0

Mam aktualnie taki problem, że mam w kodzie mapę z której będzie korzystać wiele wątków (na zasadzie pobierz klucz, zmodyfikuj wartość).

Jak to rozwiązać by nie było problemów z zakleszczaniem i innymi problemami?

0
Shalom napisał(a):

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html

Czy jak np. W Springu mam tą mape jako pole wi beanie typu singleton i modyfikuje ją requestami i rowniez jakims schedulerem i w obrębie klasy przekazuje ją do metod jako argument, to czy na pewno to wszystko jest 'bezpieczne'?

0

To zależy, jeżeli potrzebujesz dostępu do pojedynczego elementu mapy to concurrent hash mapa starczy, jeżeli np. potrzebujesz dostępu do całej mapy żeby ją przeiterować z pewnością, że nikt do niej nic nie włoży ani z niej nic nie wyjmie to musisz się synchronizować

0
  1. Sugeruję najpierw poczytanie o tym wielowątkowości w Javie
  2. Po 2 jest w dokumentacji jest wszystko napisane
0

To zależy co rozumiemy przez "bezpieczna". Na pewno się nie wywali, ale jej działanie jest specyficzne i trzeba to rozumieć. Operacje na tej mapie odzwierciedlają "aktualny" stan, który może się zmieniać w trakcie wykonywania operacji.

0

Za rzadko robię coś takiego. :/ Poradzcie proszę.

  1. Jest sobie klasa, która jest beanem typu singleton
  2. W tej klasie jest obsłużona logika CRUDa, który działa sobie na tej mapie
  3. Dodatkowo jest scheduler, który co parę minut updatuje to co znajduje się w tej mapie

ten scheduler musi w takim razie miec synchronized na tym updacie ?

1

Z tego co piszesz, raczej nie i CHM ci starczy. Po krótce CHM jest spoko, jeżeli chcesz pobrać dany obiekt z mapy lub go tam włożyć. Np. sprawdzasz czy masz w keszu taką, a taką wartość, a jak nie to ją wrzucasz - spoko. CHM nie jest dobrym rozwiązaniem gdy potrzebujesz pełnej synchronizacji, bo np. liczysz aktualny stan wpłat przez klientów - wtedy musisz się synchronizować, bo nie będziesz miał poprawnego wyniku gdy obliczysz 10 wpłat, a ktoś w tym czasie popełnił jedenastą

0
hcubyc napisał(a):

Z tego co piszesz, raczej nie i CHM ci starczy. Po krótce CHM jest spoko, jeżeli chcesz pobrać dany obiekt z mapy lub go tam włożyć. Np. sprawdzasz czy masz w keszu taką, a taką wartość, a jak nie to ją wrzucasz - spoko. CHM nie jest dobrym rozwiązaniem gdy potrzebujesz pełnej synchronizacji, bo np. liczysz aktualny stan wpłat przez klientów - wtedy musisz się synchronizować, bo nie będziesz miał poprawnego wyniku gdy obliczysz 10 wpłat, a ktoś w tym czasie popełnił jedenastą

Podczas tego schedulowanego updatu sprawdzana jestt LocalDateTime w zapisanych obiektach i sprawdzane z warunkiem "czy już nadaje się do updatu" .
Gdyby czasem coś się "nie załapało" na update to nie widziałbym w tym szczególnego problemu bo wpadnie następnym razem...

ale wydaje mi się, że powinno działać ok.

0

Jest jeszcze Collections.synchronizedMap(m), ale wystarczy o tym wiedzieć tyle, że ConcurrentHashMap jest nowsze i lepsze :) (ConcurrentHashMap vs Synchronized HashMap).

2

synchronizedMap jest synchronizowana na poziomie jednej operacji. CHM może być bezpiecznie obrabiana z wielu wątków. Żadna mapa jednak nie sprawi, że sekwencja operacji będzie atomowa (semantycznie niepodzielna). To znaczy, że jeśli mamy np kod:

int wartość = mapa.get("klucz");
mapa.put("klucz", wartość + 1);

odpalany z dwóch wątków to wynik jest niedeterministyczny obojętnie jakiego typu mapy użyjemy. Aby mieć determinizm trzeba zrobić albo pesymistyczne blokowanie:

synchronized (mapa) {
  int wartość = mapa.get("klucz");
  mapa.put("klucz", wartość + 1);
}

Albo optymistyczne blokowanie (ale tutaj trzeba użyć mapy thread-safe np synchronizedMap lub ConcurrentMap):

do {
  int oldValue = mapa.get("klucz");
  int newValue = oldValue + 1;
} while (!mapa.replace("klucz", oldValue, newValue));

Blokowanie optymistyczne jest w miarę proste w użyciu i ogólnie wydajniejsze pod warunkiem, że blokujesz tylko jeden zasób. Tutaj jest to wartość pod kluczem "klucz". Gdybyś chciał zrobić operację na wielu synchronizowanych wartościach to optymistyczne blokowanie już nie byłoby takie ładne i proste.

Poza tym zrobienie synchonized (mapa) { cośtam; } nie oznacza, że dostęp do mapy jest w pełni sychronizowany zewsząd. By tak było to wszelkie operacje na danej mapie muszą być synchronizowane z użyciem tego samego monitora.

Przykładowo jeśli jeden wątek odpala:

synchronized (mapa) {
  int wartość = mapa.get("klucz");
  mapa.put("klucz", wartość + 1);
}

a drugi wątek odpala

int wartość = mapa.get("klucz");
mapa.put("klucz", wartość + 1);

To i tak wynik będzie niederministyczny, bo drugi wątek się nie synchronizuje na monitorze mapa (w Javie każdy obiekt posiada monitor).

Nie trzeba też używać monitora obiektu do którego dostęp chcemy synchronizować. Często spotykanym wzorcem jest coś takiego:

synchronized (jakiśInnyObiekt) {
  int wartość = mapa.get("klucz");
  mapa.put("klucz", wartość + 1);
}

Ważne jest natomiast by używać tego samego monitora z każdego wątku z którego chcemy obrabiać obiekt z danymi.

1 użytkowników online, w tym zalogowanych: 0, gości: 1