Article View: pl.comp.lang.java
Article #154018Re: Wielowatkowość i Singleton
From: tomjas
Date: Fri, 30 May 2014 19:23
Date: Fri, 30 May 2014 19:23
166 lines
6249 bytes
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-mailReferences:
<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