Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:19.9.2016 12:06

Zdravíčko

není to tak dávno, co jsem sem postnul ticket, na synchronizaci vláken. Na netu jsem nenašel tu nejpodstatnější věc, ohledně synchronizace - že zámek nemůže odkazovat na this jednotlivého vlákna. Ve všech toturiálech mají všude zámky this, ale ty neodkazují na instanci potomka Thread nebo implementující třídy Runnable, ale na samostatné vlákno, což logicky nezesynchronizuje, ale opět jedou všechna vlákna současně.
Tenhle kód funguje na 100 x 100% :

public class SynchronizedThread implements Runnable {

    private Thread thread;
    private static final Object OBJECT = new Object();
    private static int number = 1;

    public static void main(String[] args) {
        new SynchronizedThread().start();
        new SynchronizedThread().start();
        new SynchronizedThread().start();
    }

    public void start() {
        if (thread== null || !thread.isAlive()) {
            thread= new Thread(this);
            thread.start();
        }
    }

    @Override
    public void run() {
        synchronized (OBJECT) {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException exp) {

                }
            }
            System.out.println(number + ". thread DONE!");
            number++;
        }
    }
}

Je to fakt paradox, že v tomto stačí od toho atributu třídy

private static final Object OBJECT = new Object();

odstranit staticu a pak jsou vlákna opět souběžné:)

No a teď k problému. Výše uvedený kód funguje jak má a já jsem spokojený. Ale jako vždy - i když to funguje, tak jsem do toho šťoural a našel jsem jednu logickou chybku. Když k výše uvedenému kódu přidám další instance, které si pojmenuji a spustím je (viz následující kód):

public class SynchronizedThread implements Runnable {

    private Thread thread;
    private static Object object = new Object();
    private static int number = 1;
    private String name;

    public static void main(String[] args) {
        final Object o = new Object();
        synchronized (o) {
            new SynchronizedThread("a").start();
            new SynchronizedThread("b").start();
            new SynchronizedThread("c").start();
            new SynchronizedThread("d").start();
            new SynchronizedThread("e").start();
            new SynchronizedThread("f").start();
        }
    }

    public SynchronizedThread(String name) {
        this.name = name;
    }

    public void start() {
        if (thread == null || !thread.isAlive()) {
            thread = new Thread(this);
            thread.start();
        }
    }

    @Override
    public void run() {
        synchronized (object) {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException exp) {

                }
            }
            System.out.println(number + ". thread DONE! Its thread named " + getName());
            number++;
        }
    }

    public String getName() {
        return name;
    }
}

tak program sice funguje, ale output je:

run:
0
1
2
3
4
5
6
7
8
9
1. thread DONE! Its thread named a
0
1
2
3
4
5
6
7
8
9
2. thread DONE! Its thread named f
0
1
2
3
4
5
6
7
8
9
3. thread DONE! Its thread named e
0
1
2
3
4
5
6
7
8
9
4. thread DONE! Its thread named d
0
1
2
3
4
5
6
7
8
9
5. thread DONE! Its thread named c
0
1
2
3
4
5
6
7
8
9
6. thread DONE! Its thread named b
BUILD SUCCESSFUL (total time: 6 seconds)

a f e d c b. Zkoušel jsem experimentovat a po důkladné analýze jsem dospěl k závěru, že vždycky se v takovémto případě spustí první vlákno a pak v pořadí od posledního až po druhé. Přidával jsem a ubíral nové instance - výsledek stejný. Zkoušel jsem všechny nové instance synchronizovat - výsledek stejný, zkoušel jsem synchronizovat každou novou instanci zvlášť - výsledek stejný.
Typická otázka deva - W T F!!!! může mi někdo vysvětlit, co je tohle za binární logiku???

Odpovědět 19.9.2016 12:06
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
patrik.valkovic
Šéfredaktor
Avatar
Odpovídá na Lubor Pešek
patrik.valkovic:19.9.2016 14:46

Protože synchronizuješ celou funkci. A v synchronizovaném kódu to i uspíš. Vytořít tedy první instanci, tu spustíš a vstouí do synchronizovaného kódu. Tím umožníš spustit další vlákna, protože čekají až první vlákno zámek uvolní. No a vypadá to, že zámek vlákna získávají v opačném pořadí než přišli, tedy až první vlákno uvolní zámek, potom zámek vezme vlákno poslední, předposlední atd. Řekl bych, že je to otázka implementace v rámci JVM.

Nahoru Odpovědět 19.9.2016 14:46
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
patrik.valkovic
Šéfredaktor
Avatar
Odpovídá na Lubor Pešek
patrik.valkovic:19.9.2016 14:48

A s tou statikou je to jasné. Pokud nebude proměnná statická, potom v každé instanci bude jiný objekt. Každé vlákno tedy bude zamykat objekt ve své konkrétní instanci.

Nahoru Odpovědět  +1 19.9.2016 14:48
Nikdy neumíme dost na to, abychom se nemohli něco nového naučit.
Avatar
Lubor Pešek
Člen
Avatar
Odpovídá na patrik.valkovic
Lubor Pešek:19.9.2016 16:02

jj ale je to děsivé - jedno slovíčko a jak to úplně změní celou podstatu běhu programu:) toto je fakt jasné, jen jsem na to chtěl poukázat, jak je v tomto případě mocné:)

Nahoru Odpovědět 19.9.2016 16:02
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Lubor Pešek
Člen
Avatar
Odpovídá na patrik.valkovic
Lubor Pešek:19.9.2016 16:04

A jinak jak to tedy řešit? přes join? prostě, jak bys to napsal (třeba i jiným způsobem), aby to vykonalo požadovanou funkci (tj. spustit popořadě všechna vlákna pěkně po sobě)

Nahoru Odpovědět 19.9.2016 16:04
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
coells
Redaktor
Avatar
Odpovídá na Lubor Pešek
coells:19.9.2016 16:59
Typická otázka deva - W T F!!!!

To je typická otázka jen pro deva, který si o tom nic nepřečte.

Pořadí nebo priorita vláken během získávání zámku je negarantovaná.
To, že se ti to spouští v tomhle pořadí, je náhoda.
To, že se to pokaždé spustilo v tomhle pořadí, je náhoda.

Pokud chceš vlákna vykonávat v pevně definovaném a blokovaném pořadí, nepoužívej vlákna.

Pokud bys ten svůj program chtěl donutit fungovat v zadaném pořadí, mohlo by tě napadnout použít ReentrantLock(fa­ir=true), který používá FIFO politiku a pořadí dodrží.

Jenže ani to nebude stačit, protože neexistuje žádná garance, že vytvořené vlákno bude ihned spuštěno.
Tím pádem není ani jisté, že vlákna o zámek žádají v pořadí, ve kterém je vytváříš.

V tom případě je potřeba implementovat primitivum, kterému se říká skupinová bariéra a nevím o tom, že by Java něco takového měla (což nutně neznamená, že nemá).

 
Nahoru Odpovědět  ±0 19.9.2016 16:59
Avatar
Lubor Pešek
Člen
Avatar
Odpovídá na coells
Lubor Pešek:19.9.2016 17:15

chlapče, já nejdřív hledám kde můžu na netu a až pak s hodně těžkým srdcem píšu o pomoc. Bohužel, pro threading není nikde žádný tutoriál, který by právě tento problém aspoň nějak nastínil. Spousta chytráků, jako jsi ty, napíše jednoduchý thread, který vypíše 10 čísel a považují to za kompletní sekci-.-

PS: 40x po sobě naprosto stejné pořadí 15 vláken je u tebe náhoda? už radši nepiš jo

Editováno 19.9.2016 17:16
Nahoru Odpovědět 19.9.2016 17:15
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
coells
Redaktor
Avatar
Odpovídá na Lubor Pešek
coells:19.9.2016 17:43

Chlapče :-D Číst tutoriál je narychlo, zalevno a ve slevě, vetšinou je píšou lidé, kteří se právě dozvěděli, že vlákna existují.
Pokud jsi to explicitně nestudoval na vysoké, tak se čtou knihy zabývající se multiprocessingem a multithreadingem, čte se dokumentace Javy a JVM, čte se problematika posixových vláken, která je dobře zdokumentovaná.

4000x po sobě 1000 vláken je pořád náhoda, trvalo mi 2 minuty upravit tvůj kód do podoby, abych dostal různé pořadí vláken i jejich spuštění.
Takže ano, žádná garance, jen náhoda a zrovna v tvém programu hned na dvou místech, až do Javy 1.5 by se ti tvůj program náhodně zasekával a občas nic nevypsal, občas neskončil, až potom opravili prioritizaci mapování a vytváření vláken.

 
Nahoru Odpovědět 19.9.2016 17:43
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:19.9.2016 17:48

prosím o uzavřítí ticketu - já žádal pomoc, ne hlody vola.

Nahoru Odpovědět 19.9.2016 17:48
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Avatar
Marian Benčat
Redaktor
Avatar
Marian Benčat:19.9.2016 18:12

Ten vul má ale ve většině pravdu. Threading není věc na kterou najdeš tutorial, je to poměrně rozsáhlá problematika a na její zcela pochopení je potřeba toho znát docela hodně. Ano - jsou na to i navrhove vzory a odborné knížky, které to dobře vysvětlují:) bohužel.. Taktika vyšších jazyk je poskytovat vývojářů nástroje, aby toto znát úplně nemuseli, jako jsou monitory, concurrent kolekce, synchronizační kontexty atp. To vývojář používá a pak je v šoku, že mu to u jednoho případu z milionů funguje jinak. Ideálně na produkci a v bankovním sektoru.

Akceptované řešení
+20 Zkušeností
Řešení problému
Nahoru Odpovědět 19.9.2016 18:12
"C# 3.0 (2007) volal Java 8 (2014), že chce svoje featury zpět"
Avatar
Hartrik
Redaktor
Avatar
Hartrik:19.9.2016 18:56

Souhlasím s tím co píše coells.

Jinak mohu doporučit třeba knížku Java Concurrency in Practice, to je dobrý úvod to této problematiky, nedávno jsem ji sám četl.

 
Nahoru Odpovědět 19.9.2016 18:56
Avatar
Lubor Pešek
Člen
Avatar
Lubor Pešek:22.9.2016 14:18

Vím, že je to už pasé, ale chci jen ukázat mnou požadované řešení (a pochopitelně je to jen example, dalo by se to napsat líp - třeba s využitím kolekce, cyklů apod.) ale mně šlo hlavně o to, aby to šlo posloupně.
PS: poradil mi to zkušený programátor, který sám tvrdí, že v programovacích jazycích existuje spousta slovíček, ale žádné "can't, its impossible, apod. NEEXISTUJí:)

public class SynchronizedThread implements Runnable {

    private Thread thread;
    private static Object object = new Object();
    private static int number = 1;
    private String name;

    public static void main(String[] args) {
        SynchronizedThread a = new SynchronizedThread("a");
        SynchronizedThread b = new SynchronizedThread("b");
        SynchronizedThread c = new SynchronizedThread("c");
        SynchronizedThread d = new SynchronizedThread("d");
        SynchronizedThread e = new SynchronizedThread("e");
        SynchronizedThread f = new SynchronizedThread("f");
        a.start();
        a.join();
        b.start();
        b.join();
        c.start();
        c.join();
        d.start();
        d.join();
        e.start();
        e.join();
        f.start();
        f.join();
    }

    public SynchronizedThread(String name) {
        this.name = name;
    }

    public void start() {
        if (thread == null || !thread.isAlive()) {
            thread = new Thread(this);
            thread.start();
        }
    }

    @Override
    public void run() {
        synchronized (object) {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException exp) {

                }
            }
            System.out.println(number + ". thread DONE! Its thread named " + getName());
            number++;
        }
    }

    public String getName() {
        return name;
    }

    private void join() {
        try {
            thread.join();
        } catch (InterruptedException ex) {
        }
    }
}

úpravy oproti tomu minulému:

  • instancializování
  • metoda join
Nahoru Odpovědět 22.9.2016 14:18
Existují dva způsoby, jak vyřešit problém. Za prvé vyhoďte počítač z okna. Za druhé vyhoďte okna z počítače.
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 12 zpráv z 12.