JavaRush /Blog Java /Random-PL /Poziom 26. Odpowiedzi na pytania podczas rozmowy kwalifik...
zor07
Poziom 31
Санкт-Петербург

Poziom 26. Odpowiedzi na pytania podczas rozmowy kwalifikacyjnej na temat poziomu. Część 2. Pytania 6-9, 11-12

Opublikowano w grupie Random-PL
Poziom 26. Odpowiedzi na pytania podczas rozmowy kwalifikacyjnej na temat poziomu.  Część 2. Pytania 6-9, 11-12 - 1

6. Czym jest Kankarensi?

Współbieżność to biblioteka klas w Javie zawierająca specjalne klasy zoptymalizowane do pracy na wielu wątkach. Zajęcia te są zebrane w pakiet java.util.concurrent. Można je schematycznie podzielić ze względu na funkcjonalność w następujący sposób: Poziom 26. Odpowiedzi na pytania podczas rozmowy kwalifikacyjnej na temat poziomu.  Część 2. Pytania 6-9, 11-12 - 2Kolekcje współbieżne – zbiór kolekcji, które działają wydajniej w środowisku wielowątkowym niż standardowe kolekcje uniwersalne z java.utilpakietu. Zamiast podstawowego wrappera Collections.synchronizedListblokującego dostęp do całej kolekcji, stosowane są blokady na segmentach danych lub praca jest zoptymalizowana pod kątem równoległego odczytu danych z wykorzystaniem algorytmów bez oczekiwania. Kolejki - kolejki nieblokujące i blokujące z obsługą wielowątkowości. Kolejki nieblokujące zaprojektowano z myślą o szybkości i działaniu bez blokowania wątków. Kolejki blokujące stosuje się, gdy trzeba „spowolnić” wątek „Producent” lub „Konsument”, jeśli nie są spełnione jakieś warunki, np. kolejka jest pusta lub przepełniona, albo nie ma wolnego „Konsumenta”. Synchronizatory to narzędzia pomocnicze służące do synchronizowania wątków. Stanowią potężną broń w przetwarzaniu „równoległym”. Executors - zawiera doskonałe frameworki do tworzenia pul wątków, planowania zadań asynchronicznych i uzyskiwania wyników. Blokady - reprezentuje alternatywne i bardziej elastyczne mechanizmy synchronizacji wątków w porównaniu do synchronizedpodstawowych . Atomics - klasy z obsługą operacji atomowych na prymitywach i referencjach. Źródło:waitnotifynotifyAll

7. Jakie zajęcia z Kankarensi znasz?

Odpowiedź na to pytanie jest doskonale zawarta w tym artykule . Nie widzę sensu przedrukowywania tego wszystkiego tutaj, dlatego opiszę tylko te zajęcia, z którymi miałem zaszczyt pokrótce się zapoznać. ConcurrentHashMap<K, V> - W przeciwieństwie do Hashtablebloków i synhronized, HashMapdane prezentowane są w formie segmentów podzielonych na skróty kluczy. W rezultacie dostęp do danych odbywa się za pomocą segmentów, a nie pojedynczego obiektu. Ponadto iteratory reprezentują dane przez określony czas i nie wyrzucają ConcurrentModificationException. AtomicBoolean, AtomicInteger, AtomicLong, AtomicIntegerArray, AtomicLongArray — A co jeśli w klasie trzeba zsynchronizować dostęp do jednej prostej zmiennej typu int? Można używać konstrukcji z synchronizedi podczas korzystania z operacji atomowych set/get, volatile. Ale możesz osiągnąć jeszcze więcej, używając nowych klas Atomic*. Dzięki zastosowaniu CAS operacje na tych klasach są szybsze niż w przypadku synchronizacji za pośrednictwem synchronized/volatile. Ponadto istnieją metody dodawania atomów o określoną ilość, a także zwiększania/zmniejszania.

8. Jak działa klasa ConcurrentHashMap?

Do czasu jego wprowadzenia ConcurrentHashMapprogramiści Java potrzebowali następującej implementacji mapy skrótów:
  • Bezpieczeństwo nici
  • Brak zamków na całym stole podczas uzyskiwania do niego dostępu
  • Pożądane jest, aby podczas wykonywania operacji odczytu nie było żadnych blokad tabeli
Główne pomysły na wdrożenie ConcurrentHashMapsą następujące:
  1. Elementy mapy

    W przeciwieństwie do elementów HashMap, Entryin ConcurrentHashMapsą deklarowane jako volatile. Jest to ważna funkcja, także ze względu na zmiany w JMM .

    static final class HashEntry<K, V> {
        final K key;
        final int hash;
        volatile V value;
        final HashEntry<K, V> next;
    
        HashEntry(K key, int hash, HashEntry<K, V> next, V value) {
            this .key = key;
            this .hash = hash;
            this .next = next;
            this .value = value;
         }
    
        @SuppressWarnings("unchecked")
        static final <K, V> HashEntry<K, V>[] newArray(int i) {
            return new HashEntry[i];
        }
    }
  2. Funkcja mieszająca

    ConcurrentHashMapużywana jest również ulepszona funkcja mieszająca.

    Przypomnę jak to wyglądało w HashMapJDK 1.2:

    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

    Wersja z ConcurrentHashMap JDK 1.5:

    private static int hash(int h) {
        h += (h << 15) ^ 0xffffcd7d;
        h ^= (h >>> 10);
        h += (h << 3);
        h ^= (h >>> 6);
        h += (h << 2) + (h << 14);
        return h ^ (h >>> 16);
    }

    Dlaczego istnieje potrzeba uczynienia funkcji skrótu bardziej złożoną? Tabele na mapie skrótów mają długość określoną przez potęgę dwójki. W przypadku kodów skrótu, których reprezentacje binarne nie różnią się na niskiej i wysokiej pozycji, będziemy mieli kolizje. Zwiększenie złożoności funkcji skrótu rozwiązuje ten problem, zmniejszając prawdopodobieństwo kolizji na mapie.

  3. Segmenty

    Mapa jest podzielona na N różnych segmentów (domyślnie 16, maksymalna wartość może wynosić 16 bitów i jest potęgą dwójki). Każdy segment jest bezpieczną dla wątków tabelą elementów mapy. Zwiększenie liczby segmentów zachęci operacje modyfikacyjne do objęcia wielu segmentów, zmniejszając prawdopodobieństwo zablokowania w czasie wykonywania.

  4. Poziom współbieżności

    Parametr ten wpływa na wykorzystanie karty pamięci oraz liczbę segmentów na karcie.

    Liczba segmentów zostanie wybrana jako najbliższa potęga dwójki większa niż concurrencyLevel. Obniżenie poziomu concurrencyLevel zwiększa prawdopodobieństwo, że wątki będą blokować segmenty mapy podczas zapisu. Przeszacowanie wskaźnika prowadzi do nieefektywnego wykorzystania pamięci. Jeśli tylko jeden wątek będzie modyfikował mapę, a reszta będzie czytać, zaleca się użycie wartości 1.

  5. Całkowity

    A zatem główne zalety i cechy implementacyjne ConcurrentHashMap:

    • hashmapMapa posiada interfejs interakcji podobny do
    • Operacje odczytu nie wymagają blokad i są wykonywane równolegle
    • Operacje zapisu często można wykonywać równolegle, bez blokowania
    • Podczas tworzenia wskazywany jest wymagany concurrencyLevel, ustalany na podstawie statystyk odczytu i zapisu
    • Elementy mapy mają zadeklarowaną wartość valuejakovolatile
    Źródło: Jak działa ConcurrentHashMap

9. Co to jest klasa Lock?

Aby kontrolować dostęp do współdzielonego zasobu, możemy zastosować blokady jako alternatywę dla operatora synchronicznego. Funkcja blokowania jest spakowana w formacie java.util.concurrent.locks. Najpierw wątek próbuje uzyskać dostęp do udostępnionego zasobu. Jeśli jest wolny, na nitce umieszczana jest blokada. Po zakończeniu pracy blokada udostępnionego zasobu zostaje zwolniona. Jeżeli zasób nie jest wolny i jest już na nim założona blokada, wówczas wątek czeka aż ta blokada zostanie zwolniona. Klasy Lock implementują interfejs Lock, który definiuje następujące metody:
  • void lock():czeka, aż zamek zostanie zdobyty
  • boolean tryLock():próbuje uzyskać blokadę; jeśli blokada zostanie uzyskana, zwraca wartość true . Jeśli blokada nie zostanie uzyskana, zwraca false . W przeciwieństwie do tej metody, lock()nie czeka na uzyskanie blokady, jeśli nie jest ona dostępna
  • void unlock():usuwa zamek
  • Condition newCondition():zwraca obiekt Conditionpowiązany z bieżącą blokadą
Organizacja blokowania w ogólnym przypadku jest dość prosta: aby uzyskać blokadę, wywoływana jest metoda lock(), a po zakończeniu pracy z współdzielonymi zasobami wywoływana jest metoda unlock(), która zwalnia blokadę. Obiekt Conditionumożliwia zarządzanie blokowaniem. Z reguły do ​​pracy z blokadami używana jest klasa ReentrantLockz pakietu , java.util.concurrent.locks.która implementuje interfejs Lock. Przyjrzyjmy się używaniu interfejsu API Java Lock na przykładzie małego programu: Załóżmy, że mamy klasę Resourcez kilkoma metodami bezpiecznymi dla wątków i metodami, w których bezpieczeństwo wątków nie jest wymagane.
public class Resource {

    public void doSomething(){
        // пусть здесь происходит работа с базой данных
    }

    public void doLogging(){
        // потокобезопасность для логгирования нам не требуется
    }
}
Weźmy teraz klasę, która implementuje interfejs Runnablei używa metod klasowych Resource.
public class SynchronizedLockExample implements Runnable{

    // экземпляр класса Resource для работы с методами
    private Resource resource;

    public SynchronizedLockExample(Resource r){
        this.resource = r;
    }

    @Override
    public void run() {
        synchronized (resource) {
            resource.doSomething();
        }
        resource.doLogging();
    }
}
Przepiszmy teraz powyższy program, używając interfejsu Lock API zamiast pliku synchronized.
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// класс для работы с Lock API. Переписан с приведенной выше программы,
// но уже без использования ключевого слова synchronized
public class ConcurrencyLockExample implements Runnable{

    private Resource resource;
    private Lock lock;

    public ConcurrencyLockExample(Resource r){
        this.resource = r;
        this.lock = new ReentrantLock();
    }

    @Override
    public void run() {
        try {
            // лочим на 10 секунд
            if(lock.tryLock(10, TimeUnit.SECONDS)){
            resource.doSomething();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            //убираем лок
            lock.unlock();
        }
        // Для логгирования не требуется потокобезопасность
        resource.doLogging();
    }

}
Jak widać z programu, używamy tej metody tryLock(), aby mieć pewność, że wątek będzie czekał tylko przez określoną ilość czasu. Jeśli nie uzyska blokady obiektu, po prostu loguje się i wychodzi. Kolejny ważny punkt. Musisz użyć bloku, try-finallyaby mieć pewność, że blokada zostanie zwolniona, nawet jeśli metoda doSomething()zgłosi wyjątek. Źródła:

11. Co to jest muteks?

Mutex to specjalny obiekt służący do synchronizacji wątków/procesów. Może przyjmować dwa stany - zajęty i wolny. W uproszczeniu muteks jest zmienną boolowską, która przyjmuje dwie wartości: zajęty (prawda) i wolny (fałsz). Kiedy wątek chce mieć wyłączną własność obiektu, oznacza swój muteks jako zajęty, a kiedy zakończy z nim pracę, oznacza swój muteks jako wolny. Do każdego obiektu w Javie dołączony jest muteks. Tylko maszyna Java ma bezpośredni dostęp do muteksu. Jest ukryty przed programistą.

12. Co to jest monitor?

Monitor to specjalny mechanizm (kawałek kodu) - dodatek do mutexu, który zapewnia poprawną współpracę z nim. Przecież nie wystarczy zaznaczyć, że obiekt jest zajęty; musimy także zadbać o to, aby inne wątki nie próbowały wykorzystać zajętego obiektu. W Javie monitor jest implementowany przy użyciu słowa kluczowego synchronized. Kiedy piszemy zsynchronizowany blok, kompilator Java zastępuje go trzema fragmentami kodu:
  1. Na początku bloku synchronizeddodawany jest kod oznaczający muteks jako zajęty.
  2. Na końcu bloku synchronizeddodawany jest kod oznaczający muteks jako wolny.
  3. Przed blokiem synchronizeddodawany jest kod sprawdzający czy muteks jest zajęty, wówczas wątek musi poczekać na jego zwolnienie.
Część 1
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION