Współbieżność, kolejki blokujące (Java 7) — 1

"Cześć, Amigo!"

"Cześć, Kim!"

„Dzisiaj opowiem wam o współbieżności”.

" Concurrency to biblioteka klas Java, która zawiera specjalne klasy, które zostały zoptymalizowane do pracy z wieloma wątkami. To bardzo ciekawy i obszerny temat. Ale dzisiaj dostaniemy tylko wprowadzenie. Pakiet nazywa się java.util. pakiet równoległy. Opowiem o kilku ciekawych zajęciach."

„ Typy atomowe ” .

„Wiesz już, że nawet count++ nie jest operacją bezpieczną dla wątków. Kiedy zmienna jest zwiększana o 1, w rzeczywistości mają miejsce trzy operacje. W rezultacie może wystąpić konflikt, gdy zmienna zostanie zmieniona”.

„Tak, Ellie powiedziała mi nie tak dawno temu:”

Wątek 1 Wątek 2 Wynik
register1 = count;
register1++;
count = register1;
register2 = count;
register2++;
count = register2;
register1 = count;
register2 = count;
register2++;
count = register2;
register1++;
count = register1;

„Dokładnie. Następnie Java dodała typy danych, aby wykonywać te operacje jako jeden, tj. atomowo (atom jest niepodzielny).”

„Na przykład Java ma AtomicInteger, AtomicBoolean, AtomicDouble itp.”

„Załóżmy, że musimy stworzyć klasę «licznik»:”

Przykład
class Counter
{
 private int c = 0;

 public void increment()
 {
  c++;
 }

 public void decrement()
 {
  c--;
 }

 public int value()
 {
  return c;
 }
}

„Jak sprawić, by obiekty tej klasy były bezpieczne dla wątków?”

„Cóż, zsynchronizowałbym wszystkie metody i skończyłbym z tym:”

Przykład
class synchronized Counter
{
 private int c = 0;

 public synchronized void increment()
 {
  c++;
 }

 public synchronized void decrement()
 {
  c--;
 }

 public synchronized int value()
 {
  return c;
 }
}

„Dobra robota. Ale jak by to wyglądało, gdybyśmy używali typów atomowych:”

Przykład
class AtomicCounter
{
 private AtomicInteger c = new AtomicInteger(0);

 public void increment()
 {
  c.incrementAndGet();
 }

 public void decrement()
 {
  c.decrementAndGet();
 }

 public int value()
 {
  return c.get();
 }
}

„Twoja klasa i moja klasa działają w ten sam sposób, ale klasa z AtomicInteger działa szybciej”.

– No, czy to mała różnica?

„Tak. Bazując na moim doświadczeniu, zawsze zalecam prowadzenie z synchronizacją. Dopiero po napisaniu całego kodu aplikacji i rozpoczęciu procesu optymalizacji należy zacząć przepisywać kod, aby używał typów atomowych. Ale w każdym razie chciałem, żebyś wiedzieć, że takie typy istnieją. Nawet jeśli nie używasz ich aktywnie, zawsze istnieje szansa, że ​​natrafisz na kod tam, gdzie są używane”.

„Zgadzam się. To ma sens”.

„Przy okazji, czy zauważyłeś, że typy atomowe nie są niezmienne ? W przeciwieństwie do standardowej klasy Integer , AtomicInteger zawiera metody zmiany swojego stanu wewnętrznego”.

„Rozumiem. Tak samo jak String i StringBuffer ”.

"Tak, coś w tym stylu."

Kolekcje bezpieczne dla wątków ” .

„Jako przykład takiej kolekcji mogę przedstawić ConcurrentHashMap. Jak sprawić, by HashMap był bezpieczny dla wątków?”

„Zsynchronizować wszystkie jego metody?”

„Oczywiście, ale teraz wyobraź sobie, że masz jedną taką SynchronizedHashMap i dziesiątki wątków uzyskujących do niej dostęp. I sto razy na sekundę do mapy dodawany jest nowy wpis, a w trakcie tego procesu cały obiekt jest blokowany do odczytu i zapisu”.

„Cóż, to jest standardowe podejście. Co możesz zrobić?”

„Twórcy Javy wymyślili kilka fajnych rzeczy”.

„Po pierwsze, przechowują dane w ConcurrentHashMap w jednym bloku, ale dzielą je na części zwane„ segmentami ”. A kiedy ktoś zmienia dane w ConcurrentHashMap, blokujemy tylko dostęp do segmentu, a nie cały obiekt. słowa, wiele wątków może jednocześnie zmieniać obiekt”.

„Po drugie, czy pamiętasz, że nie możesz iterować elementów listy/mapy i jednocześnie zmieniać listy? Taki kod rzuci wyjątek:”

Nie przeglądaj elementów kolekcji w pętli i jednocześnie jej nie zmieniaj
HashMap<String, Integer> map = new HashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key) == 0)
  map.remove(key);
}

„Ale w ConcurrentHashMap możesz:”

Nie iteruj po elementach kolekcji w pętli i jednocześnie ją zmieniaj”
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<String, Integer>();

for (String key: map.keySet())
{
 if (map.get(key) == 0)
  map.remove(key);
}

„Pakiet równoległy ma wiele zalet. Musimy tylko bardzo dobrze zrozumieć te klasy, aby z nich korzystać”.

„Rozumiem. Dzięki, Kim. To naprawdę ciekawe zajęcia. Mam nadzieję, że kiedyś opanuję je jak wirtuoz”.