🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: pl.comp.lang.java
Article #154018

Re: Wielowatkowość i Singleton

#154018
From: tomjas
Date: Fri, 30 May 2014 19:23
166 lines
6249 bytes
On Fri, 30 May 2014 20:51:11 +0200, Arivald wrote:
> On 2014-05-30 19:21, tomjas wrote:
>> Witam.
>>
>> Tak w ramach ćwiczeń postanowiłem sobie poćwiczyć różne wersje wzorca
>> singleton. Poniżej kod testujący singleton w najprostszej wersji
>> Lazy-Initialization (oczywiście abstrahujemy od jakości wersji wzorca
>> - wiem, że nie jest to najlepsze rozwiązanie). A więc sam signleton:
>>
>> #v+
>> class Counter {
>>      private static Counter INSTANCE;
>>
>>      private Counter(){}
>>
>>      public static synchronized Counter getInstance() {
>>          if (INSTANCE == null) {
>>              INSTANCE = new Counter();
>>          }
>>          return INSTANCE;
>>      }
>> }
>
> To nie jest najwydajniejsza implementacja singletona z lazy initem. 
> Najlepsza w Javie jest oparta na wewnętrznej klasie.
>

Dlatego napisałem, że "wiem, że nie jest to najlepsze rozwiązanie" :)
Swoją drogą czytałem, że najlepsza implementacja oparta jest na typie
enum.

> public class Something {
>      private Something() {}
>
>      private static class LazyHolder {
>          private static final Something INSTANCE = new Something();
>      }
>
>      public static Something getInstance() {
>          return LazyHolder.INSTANCE;
>      }
> }
>
> Ten pattern gwarantuje leniwą inicjalizację, i nie ma straty wydajnosći 
> na synchronizowaniu.
>
>>
>> teraz klasa, która zawiera kod w tworzonych wątkach. Do konstruktora
>> przekazuję hashmapę, w której będę zliczał obiekty o takim samym
>> hashkodzie:
>>
>>
>> #v+
>> class CounterThread implements Runnable {
>>
>>      Map<Integer, Integer> map;
>>
>>      public CounterThread(Map<Integer, Integer> map) {
>>          this.map = map;
>>      }
>>
>>      @Override
>>      public void run() {
>>          Counter cnt = Counter.getInstance();
>>          int hash = cnt.hashCode();
>
>>          Integer freq = map.get(hash);
>>          map.put(hash, (freq == null) ? 1 : freq + 1);
>
> Te dwie linie to popularny błąd wiary w cudowne właściwości 
> Collections.synchronizedMap().
> Pomiędzy map.get(hash) a map.put(hash, ..) inny wątek może zmienić mapę, 
> i dane lecą w kosmos (czyli któryś z wątków zapisze do mapy wartość 
> która już tam jest, choć wydaje mu się że ją zwiększył).

No właaaaśśśnieee! Wielkie dzięki! Dalsze wyjaśnienia poniżej stają się
jasne.

>
> Żeby było poprawnie i bezpiecznie to te dwie linie należy 
> zsynchronizować na mapie, np tak:
>
> class CounterThread implements Runnable {
>
>      Map<Integer, Integer> map;
>
>      public CounterThread(Map<Integer, Integer> map) {
>          this.map = map;
>      }
>
>      @Override
>      public void run() {
>          Counter cnt = Counter.getInstance();
>          int hash = cnt.hashCode();
>          synchronized(map) {
>            Integer freq = map.get(hash);
>            map.put(hash, (freq == null) ? 1 : freq + 1);
>          }
>      }
> }
>
> ... i w takim wypadku mapa nie musi być już przepiszczana poprzez 
> Collections.synchronizedMap().
>
> Albo użyj synchronizowanej mapy, w wersji która dla get nieznanego 
> klucza automatycznie tworzy parę klucz/wartość, a jako wartość użyj 
> AtomicInteger.
> Kod wywołania wyglądał by wtedy tak:
>
>      public void run() {
>          Counter cnt = Counter.getInstance();
>          int hash = cnt.hashCode();
> 	map.get(hash).incrementAndGet();
>      }
>
>
>
>>      }
>> }
>> #v-
>>
>> No i klasa tworząca 1000 wątków.
>>
>> #v+
>> public class Main {
>>      public static void main(String[] args) {
>>
>>          Map<Integer, Integer> map = Collections
>>                  .synchronizedMap(new HashMap<Integer, Integer>());
>>
>>          for (int i = 0; i < 1_000; i++) {
>>              new Thread(new CounterThread(map)).start();
>>          }
>>
>>          try {
>>              Thread.sleep(5000);
>>              System.out.println(map);
>>          } catch (InterruptedException e) {
>>          }
>>      }
>> }
>> #v-
>>
>>
>> Dla pewności daję głównegmu wątkowi odpocząć 5 sek i sprawdzam hashmapę.
>> A wynik wygląda np. tak:
>>
>> {1704732977=897}
>>
>>
>> Rzecz jasna za każdym wywołaniem zmienia się wartość hashkodu i ich
>> liczba, ale - i tu moje pytanie - co z resztą wątków tworzących obiekty
>> singletona (Counter) i licząca dla nich haskody? Przecież powinno być
>> ich 1000. Gdzie popełniam błąd, bo już mi monitor oczy wypala
>> od wpatrywania?
>>
>
> Z powodu błędów i wyścigu wątków nie każdy wątek zdołał zwiększyć licznik.
>
>> Dodatkowo, dlaczego jeśli utworzę zwykłą hashmapę bez synchronizacji, to
>> jej odczyt wygląda jak powyżej, a odczyt jej wielkości (map.size())
>> potrafi pokazać 2-5 elementów?
>
> Bo zwykła mapa bez synchronizacji nie jest bezpieczna przy korzystaniu 
> wielowątkowym, i cuda się dzieją => zachowanie niezdefiniowane.
>
Technical Details
Message-ID: <slrnlohmle.lna.tomjas@darkstar.example.net>
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!agh.edu.pl!news.agh.edu.pl!newsfeed2.atman.pl!newsfeed.atman.pl!news.nask.pl!news.nask.org.pl!newsfeed.pionier.net.pl!news.glorb.com!peer01.iad.highwinds-media.com!news.highwinds-media.com!feed-me.highwinds-media.com!nx02.iad01.newshosting.com!newshosting.com!newsfeed.neostrada.pl!unt-exc-02.news.neostrada.pl!unt-spo-a-02.news.neostrada.pl!news.neostrada.pl.POSTED!not-for-mail
References: <slrnlohfgo.ngq.tomjas@darkstar.example.net> <lmajv8$3sl$1@news.dialog.net.pl>

Show Full Headers
Complete Article Headers
Path: mbox-mo1.pugleaf.net!news-archive.icm.edu.pl!agh.edu.pl!news.agh.edu.pl!newsfeed2.atman.pl!newsfeed.atman.pl!news.nask.pl!news.nask.org.pl!newsfeed.pionier.net.pl!news.glorb.com!peer01.iad.highwinds-media.com!news.highwinds-media.com!feed-me.highwinds-media.com!nx02.iad01.newshosting.com!newshosting.com!newsfeed.neostrada.pl!unt-exc-02.news.neostrada.pl!unt-spo-a-02.news.neostrada.pl!news.neostrada.pl.POSTED!not-for-mail
Newsgroups: pl.comp.lang.java
From: tomjas <tomjas@WYTNIJTOpoczta.onet.pl>
Subject: Re: Wielowatkowość i Singleton
References: <slrnlohfgo.ngq.tomjas@darkstar.example.net> <lmajv8$3sl$1@news.dialog.net.pl>
Reply-To: tomjas
User-Agent: slrn/1.0.1 (Linux)
Mime-Version: 1.0
Message-ID: <slrnlohmle.lna.tomjas@darkstar.example.net>
Date: 30 May 2014 19:23:27 GMT
Lines: 166
Organization: Telekomunikacja Polska
NNTP-Posting-Host: ip-37-209-151-206.free.aero2.net.pl
X-Trace: 1401477807 unt-rea-a-02.news.neostrada.pl 2162 37.209.151.206:41577
X-Complaints-To: abuse@news.neostrada.pl
X-Received-Bytes: 5956
X-Received-Body-CRC: 4230645818
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit