Toon posts:

[JAVA] Thread & notify() - op welk niveau wekken?

Pagina: 1
Acties:

Verwijderd

Topicstarter
Hoi!

Stel je hebt volgende simpele thread class:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class myThread extends Thread
{
    private String m_name;
    
    public myThread(String name)
    {
        m_name = name;
        
        this.start();
    }
    
    public void run()
    {
        while (true)
            runMe();
    }
    
    public synchronized void runMe()
    {
        try
        {
            wait();
            
            System.out.println("[" + m_name + "] na wait");
        }
        catch (InterruptedException e)
        {
            
        }
    }
        
    public synchronized void wekNotify()
    {
        notify();   
    }
}


Nu twijfel ik over de werking van notify():

- notify() wekt de wait() in het huidige instantie object van myThread; met instantie object bedoel ik dus het object waarop wekNotify() wordt opgeroepen
- notify() wekt de wait() in een willekeurige instantie object van myThread, en niet noodzakelijk de huidige;

Ik wil het volgende maken: ik heb een class Manager en een aantal (onbepaald) Workers. Workers zijn dus threads die 1 of andere operatie uitvoeren in de achtergrond. De Workers zeggen tegen de Manager dat ze klaar zijn om een taak uit te voeren. De Manager reageert hier (eventueel) op door het oproepen van de functie wekNotify(): hetObjectDatKlaarIs.wekNotify(). Hier zit ik dus vast ivm. de semantiek van notify(): wat als 2 Workers zich aangemeld hebben om te werken, de Manager kiest er de beste uit (hij wil dus dat er maar 1 van de 2 Workers gaat werken: de beste), en roept op deze wekNotify() op: besteObjectVanDe2.wekNotify()

-> als notify() de eerste betekenis heeft, is er geen probleem: er is garantie dat de beste Worker de taak toegewezen krijgt, want de wait() van besteObjectVanDe2 wordt gewekt.
-> als notify() de tweede betekenis heeft, is er wel een probleem: er is geen garantie dat de beste Worker de taak toegewezen krijgt, want de wait() van besteObjectVanDe2 wordt niet noodzakelijk gewekt.

Hoe zit dat nu? In de JAVA docs staat:
Wakes up a single thread that is waiting on this object's monitor. If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation. A thread waits on an object's monitor by calling one of the wait methods.
Dus mag ik hieruit besluiten dat notify() de tweede betekenis heeft, en eender welk object kan wakker maken?

Ik heb ook een test programmatje gemaakt:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main
{
    public static void main(String[] args)
    {
        myThread thread1 = new myThread("Thread 1");
        myThread thread2 = new myThread("Thread 2");
        myThread thread3 = new myThread("Thread 3");
        
        /* notify */
        try { Thread.sleep(1000); } catch (InterruptedException e) { }

        thread1.wekNotify();
        
        try { Thread.sleep(1000); } catch (InterruptedException e) { }

        thread2.wekNotify();
        
        try { Thread.sleep(1000); } catch (InterruptedException e) { }

        thread3.wekNotify();
    }
}
Ik heb het zeker 50 keer uitgevoerd, en altijd was de volgorde: 1, 2, 3. Is dat dan toeval? Want hier wekt threadx.wekNotify() altijd de wait() in threadx, en dat wil ik nu net!

En mijn tweede vraag: als notify() dus willekeurig is (tweede betekenis), hoe kan ik dan garanderen dat het object wordt opgeroepen dat IK wil, en niet wat de thread manager toevallig kiest? Want notify() lijkt hier dan totaal zinloos?

thanks

[ Voor 3% gewijzigd door Verwijderd op 15-10-2003 17:41 ]


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Het stukje wat je quote gaat erover dat als er meerdere threads wait op een object aanroepen dat er door notify maar een thread wordt geresumed. Als er twee threads zijn moet je 2 maal notify aanroepen om ze beide te resumen. Als je ze beide tegelijk wil resumen dan moet je notify all gebruiken.

Dit gaat dus echter wel per object. In MyThread roep je wait en notify op die instantie van het object aan dus zal je in dit geval altijd 1, 2, 3 als uitvoer krijgen. Maar als je bijvoorbeeld in een andere thread dit zou doen
Java:
1
thread1.wait();

dan kan het wel anders gaan lopen. Als je zeker wil zijn dat altijd die thread geresumed wordt dan kan je het beter zo doen
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class myThread extends Thread
{
    private String m_name;
    private Object myLock = new object();
    
    public myThread(String name)
    {
        m_name = name;
        
        this.start();
    }
    
    public void run()
    {
        while (true)
            runMe();
    }
    
    public synchronized void runMe()
    {
        try
        {
            myLock.wait();
            
            System.out.println("[" + m_name + "] na wait");
        }
        catch (InterruptedException e)
        {
            
        }
    }
        
    public synchronized void wekNotify()
    {
        myLock.notify();    
    }
}


In dit geval weet je zeker dat er niet stiekum een andere thread de monitor van je object af kan pakken.

offtopic:
mmzz. weer lekker duidelijk uitgelegd :P

edit:

Glimi zegt het denk wat duidelijker maar het is wel zo ongeveer wat ik ook probeerde te zeggen

[ Voor 21% gewijzigd door Woy op 15-10-2003 18:18 . Reden: Code voorbeeldje aangepast ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Hey ;)
- notify() wekt de wait() in het huidige instantie object van myThread; met instantie object bedoel ik dus het object waarop wekNotify() wordt opgeroepen
- notify() wekt de wait() in een willekeurige instantie object van myThread, en niet noodzakelijk de huidige;
notify() wekt een thread die zit te wachten op een lock van het object waarin notify() aangeroepen wordt. Je kunt het zien als een soort wachtkamer.
Stel een thread komt een wait tegen en wordt dus in de wachtkamer van het object geplaatst waarin de wait-aanroep stond. Bij een notify van een andere thread, binnen de code van dat object, wordt de thread uit de wachtkamer gehaald en mag weer lekker verder met de code na de wait() aanroep.

Beter zou je het dus kunnen zeggen:
- Een aanroep van notify() binnen een object, wekt één Thread die door hetzelfde object dmv wait() in slaapstand is gezet.
Ik wil het volgende maken: ik heb een class Manager en een aantal (onbepaald) Workers. Workers zijn dus threads die 1 of andere operatie uitvoeren in de achtergrond. De Workers zeggen tegen de Manager dat ze klaar zijn om een taak uit te voeren. De Manager reageert hier (eventueel) op door het oproepen van de functie wekNotify(): hetObjectDatKlaarIs.wekNotify(). Hier zit ik dus vast ivm. de semantiek van notify(): wat als 2 Workers zich aangemeld hebben om te werken, de Manager kiest er de beste uit (hij wil dus dat er maar 1 van de 2 Workers gaat werken: de beste), en roept op deze wekNotify() op: besteObjectVanDe2.wekNotify()
Is het gebruik van een ThreadPool geen optie voor de Manager? Dan hoeft hij zich niet met de rompslomp bezig te houden en gewoon een Thread uit de Pool pakken.
Zie [rml][ Java] Hoe weten dat threads klaar zijn[/rml] voor meer info daarover :)
Ik heb ook een test programmatje gemaakt:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main
{
    public static void main(String[] args)
    {
        myThread thread1 = new myThread("Thread 1");
        myThread thread2 = new myThread("Thread 2");
        myThread thread3 = new myThread("Thread 3");
        
        /* notify */
        try { Thread.sleep(1000); } catch (InterruptedException e) { }

        thread1.wekNotify();
        
        try { Thread.sleep(1000); } catch (InterruptedException e) { }

        thread2.wekNotify();
        
        try { Thread.sleep(1000); } catch (InterruptedException e) { }

        thread3.wekNotify();
    }
}
Ik heb het zeker 50 keer uitgevoerd, en altijd was de volgorde: 1, 2, 3. Is dat dan toeval? Want hier wekt threadx.wekNotify() altijd de wait() in threadx, en dat wil ik nu net!
notify() en wait() werken dus zoals jij hoopte dat ze werken. Echter kijk wel uit met testen, want het kan 1000 maal goed gaan, maar de 1001ste keer kan doordat bijv je OS het opeens zwaar heeft, een hele andere Threadinterleaving gaan optreden.
En mijn tweede vraag: als notify() dus willekeurig is (tweede betekenis), hoe kan ik dan garanderen dat het object wordt opgeroepen dat IK wil, en niet wat de thread manager toevallig kiest? Want notify() lijkt hier dan totaal zinloos?

thanks
De willekeur in die quote gaat slechts op dat er willekeurig één Thread gewekt wordt (er kunnen meerdere Threads wachten op één objectlock). Als je alle Threads wil wekken die op één objectlock wachten, kun je notifyAll() gebruiken

Verwijderd

Topicstarter
Oké, ik denk dat ik het snap ;) Simpel gezegd, hoe ik het nu zie:

- als je wait() hebt, zeg je: de thread die nu loopt wacht op object 'this' (er staat eigenlijk this.wait()) dus als er in 'this' een notify() aangeroepen wordt, word ik terug gewekt.
- als je obj.wait() hebt, zeg je: de thread wacht op object 'obj', dus als er in 'obj' notify() aangeroepen wordt, word ik terug gewekt.

(ik veronderstel even dat je maar 1 wait(), dus een notify() volstaat)

Is dat samengevat juist?

edit: ok heb het even uitgetest, en het lijkt te werken zoals ik dacht, dus zoals jullie zeiden :)

Testje.java
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Testje
{
    public Testje()
    {
        myThread thread1 = new myThread(this, "Thread 1");
        myThread thread2 = new myThread(this, "Thread 2");
        myThread thread3 = new myThread(this, "Thread 3");
        
        /* wacht even tot alle threads gestart zijn - hardcoded */      
        try { Thread.sleep(1000); } catch (InterruptedException e) { }
        
        while (true)
        {
            synchronized (this)
            {
            notify();
            }
        }
    }
}
myThread.java
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class myThread extends Thread
{
    private String m_name;
    private Testje m_parent;
    
    public myThread(Testje parent, String name)
    {
        m_name = name;
        m_parent = parent;
        
        this.start();
    }
    
    public void run()
    {
        while (true)
            runMe();
    }
    
    public void runMe()
    {
        try
        {
            synchronized(m_parent) /* owner over m_parent */
            {
                m_parent.wait();
            }
            
            System.out.println("[" + m_name + "] na wait");
        }
        catch (InterruptedException e)
        {
            
        }
    }
}
Main.java
code:
1
2
3
4
5
6
7
public class Main
{
    public static void main(String[] args)
    {
        Testje test = new Testje();
    }
}
Nu wekt hij willekeurig 1 van de threads die allemaal op m_parent, het object van Testje.java, wachten.

Hoop dat ik het allemaal juist is wat ik zeg :)

bedankt voor jullie hulp

edit: ben ook over Threadpool aan het lezen, ziet er wel makkelijker uit. Maar volgens mij kan ik het niet helemaal gebruiken, want ik moet op basis van bepaalde criterea kiezen welke Worker 'de beste is'. Kan natuurlijk met priorities gaan werken, maar after all bepaalt het OS nog altijd zelf wat hij ermee moet, en welke hij dus gaat kiezen.

[ Voor 117% gewijzigd door Verwijderd op 15-10-2003 21:55 ]


  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Verwijderd schreef op 15 October 2003 @ 21:19:
Oké, ik denk dat ik het snap ;) Simpel gezegd, hoe ik het nu zie:

- als je wait() hebt, zeg je: de thread die nu loopt wacht op object 'this' (er staat eigenlijk this.wait()) dus als er in 'this' een notify() aangeroepen wordt, word ik terug gewekt.
- als je obj.wait() hebt, zeg je: de thread wacht op object 'obj', dus als er in 'obj' notify() aangeroepen wordt, word ik terug gewekt.

(ik veronderstel even dat je maar 1 wait(), dus een notify() volstaat)

Is dat samengevat juist?

edit: ok heb het even uitgetest, en het lijkt te werken zoals ik dacht, dus zoals jullie zeiden :)

Testje.java
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Testje
{
    public Testje()
    {
        myThread thread1 = new myThread(this, "Thread 1");
        myThread thread2 = new myThread(this, "Thread 2");
        myThread thread3 = new myThread(this, "Thread 3");
        
        /* wacht even tot alle threads gestart zijn - hardcoded */      
        try { Thread.sleep(1000); } catch (InterruptedException e) { }
        
        while (true)
        {
            synchronized (this)
            {
            notify();
            }
        }
    }
}
myThread.java
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class myThread extends Thread
{
    private String m_name;
    private Testje m_parent;
    
    public myThread(Testje parent, String name)
    {
        m_name = name;
        m_parent = parent;
        
        this.start();
    }
    
    public void run()
    {
        while (true)
            runMe();
    }
    
    public void runMe()
    {
        try
        {
            synchronized(m_parent) /* owner over m_parent */
            {
                m_parent.wait();
            }
            
            System.out.println("[" + m_name + "] na wait");
        }
        catch (InterruptedException e)
        {
            
        }
    }
}
Main.java
code:
1
2
3
4
5
6
7
public class Main
{
    public static void main(String[] args)
    {
        Testje test = new Testje();
    }
}
Nu wekt hij willekeurig 1 van de threads die allemaal op m_parent, het object van Testje.java, wachten.

Hoop dat ik het allemaal juist is wat ik zeg :)

bedankt voor jullie hulp

edit: ben ook over Threadpool aan het lezen, ziet er wel makkelijker uit. Maar volgens mij kan ik het niet helemaal gebruiken, want ik moet op basis van bepaalde criterea kiezen welke Worker 'de beste is'. Kan natuurlijk met priorities gaan werken, maar after all bepaalt het OS nog altijd zelf wat hij ermee moet, en welke hij dus gaat kiezen.
Volgens mij snap je het idd wel redelijk. Ik vraag me alleen af of degene die hij notified wel willekeurig is. Het is tenslotte gewoon een algoritme wat erachter zit dus het zou me niks verbazen als het FIFO is. Daar kan ik echter aan de hand van de documentatie even niks over zeggen.

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Verwijderd

Topicstarter
Ja, met willekeurig bedoel ik: ik kan het niet met volstrekste zekerheid zeggen : )

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Verwijderd schreef op 16 October 2003 @ 02:06:
Ja, met willekeurig bedoel ik: ik kan het niet met volstrekste zekerheid zeggen : )
Dat is inderdaad de goede uitleg :)

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”

Pagina: 1