>>>>> "Dimitris" == Dimitris Vyzovitis <[EMAIL PROTECTED]> writes:

    Dimitris> On 9 Feb 2001, Juergen Kreileder wrote:
    >> BTW, it's "Thread.yield()".  "Thread.currentThread().yield()"
    >> has exactly the same meaning but as Thread.yield() is a class
    >> method you should call it directly on the class.
    >> "Thread.currentThread().yield()" makes it look like yield()
    >> would be an instance method.

    Dimitris> Yes, but all operations refer to the current thread, and
    Dimitris> conceptually refer to the same object. Since java is
    Dimitris> positioned as a concurrent object oriented programming
    Dimitris> language, the class instance (actor) behaves as a
    Dimitris> forwarder for the current thread object (actor), and
    Dimitris> semantically it is perfectly legal to bypass the
    Dimitris> forwarding step and be explicit.

You don't bypass anything here.

        Thread.yield();

and
        Thread.currentThread().yield();

and even

        Thread t = null;
        t.yield();

have exactly the same meaning, they all work on the thread executing
the code.  The first one is the preferred form as it clearly shows
that that yield() is class/static method of class Thread.  The second
variant just adds useless code.  And the latter one may really confuse
some people.

To illustrate that:

Compile and disassemble the following class:

,----[ T.java ]
| public class T
| {
|     void a()
|     {
|         Thread.yield();
|     }
| 
|     void b()
|     {
|         Thread.currentThread().yield();
|     }
| 
|     void c()
|     {
|         Thread t = null;
|         t.yield();
|     }
| }
`----

,----[ javap -b -c T ]
| [...]
| Method void a()
|    0 invokestatic #2 <Method java.lang.Thread.yield()V>
|    3 return
| 
| Method void b()
|    0 invokestatic #3 <Method java.lang.Thread.currentThread()Ljava/lang/Thread;>
|    3 pop
|    4 invokestatic #2 <Method java.lang.Thread.yield()V>
|    7 return
| 
| Method void c()
|    0 aconst_null
|    1 astore_1
|    2 invokestatic #2 <Method java.lang.Thread.yield()V>
|    5 return
| [...]
`----

As you can see: All three variants end up doing a invokestatic of
exactly the same method.

If you decompile T.class you'll get this:

,----[ jad T.class ; cat T.jad ]
| [...]
|     void a()
|     {
|         Thread.yield();
|     }
| 
|     void b()
|     {
|         Thread.currentThread();
|         Thread.yield();
|     }
| 
|     void c()
|     {
|         Object obj = null;
|         Thread.yield();
|     }
| [...]
`----

This shows the useless code pretty well.


    Dimitris> Btw, it might be interesting in an smp machine to have
    Dimitris> the capability to instruct other threads to yield. The
    Dimitris> current spec makes a_thread.yield() meaningless
    Dimitris> (<any_thread>.yield() ==
    Dimitris> Thread.currentThread().yield()). This could be useful in
    Dimitris> user-space scheduler implementations and a mixed
    Dimitris> threading model.

Not really.  In these cases you want to control which thread gets cpu
time next.  Just yielding other threads doesn't give this control.
You're better off implementing such things at a lower level.

    Dimitris> As far as the correctness of yield behavior is concerned
    Dimitris> you are right (it does sth the jls correcness criteria
    Dimitris> and does not violate strong fairness conditions), but
    Dimitris> there is definetely sth instructive in the way the
    Dimitris> test_yield program behaves. If there is no I/O thread
    Dimitris> involved, then all threads get approximately equal run
    Dimitris> occurences (see test_fairness, attached).

I'm not sure what you want to show with test_fairness.  You have three
threads which all count up to the same number.  Then finally you print
out the three counters which always will be the same.  There's nothing
in this code testing fairness of scheduling.

    Dimitris> If you run test_yield with a longer sleep period, you'll
    Dimitris> notice that there is a dominant thread which gets approx
    Dimitris> as many runs as the other two. Although this does not
    Dimitris> violate the fairness conditions imposed by the jls, it
    Dimitris> is not quite what I expect given the perfectly fair
    Dimitris> behavior when no i/o is performed.

Ah, fix test_fairness and you'll see that it isn't IO related.

    Dimitris> Once again, this is not a bug in the blackdown vm since
    Dimitris> it still satisfy the jls, but the behavior of ibm's vm
    Dimitris> is more *consistent*.

[Actually, I would consider such a significant difference a bug.]

As said in my previous posting: The difference you see is is not
caused by scheduling and/or a specific Thread.yield() implementation.
You just see a comparison of _interpreted_ vs _compiled_ code.

I've attached a modified version of your initial test code.  Run it
with and without "-Dwarmup".  
Also watch the threads in top while running the tests, you won't
notice a difference between the warmup and no-warmup cases there.

Here are my results.  The "-Dwarmup" cases look pretty fair.

,----
| % java TestYield 
| r1: 2 r2: 3 r3: 3
| r1: 3 r2: 6 r3: 6
| r1: 5 r2: 9 r3: 9
| r1: 7 r2: 12 r3: 12
| r1: 8 r2: 14 r3: 14
| r1: 10 r2: 17 r3: 17
| r1: 11 r2: 20 r3: 20
| r1: 13 r2: 23 r3: 23
| r1: 14 r2: 26 r3: 26
| r1: 16 r2: 28 r3: 28
| 
| % java -Dwarmup TestYield
| r1: 3 r2: 3 r3: 3
| r1: 6 r2: 6 r3: 6
| r1: 9 r2: 9 r3: 9
| r1: 11 r2: 11 r3: 12
| r1: 14 r2: 14 r3: 14
| r1: 17 r2: 17 r3: 17
| r1: 19 r2: 20 r3: 20
| r1: 22 r2: 23 r3: 23
| r1: 25 r2: 25 r3: 26
| r1: 27 r2: 28 r3: 28
| 
| % java -server TestYield
| r1: 2 r2: 4 r3: 4
| r1: 4 r2: 9 r3: 9
| r1: 7 r2: 13 r3: 13
| r1: 8 r2: 17 r3: 17
| r1: 11 r2: 21 r3: 21
| r1: 13 r2: 26 r3: 25
| r1: 14 r2: 29 r3: 29
| r1: 17 r2: 33 r3: 34
| r1: 18 r2: 37 r3: 38
| r1: 20 r2: 42 r3: 42
| 
| % java -server -Dwarmup TestYield
| r1: 4 r2: 4 r3: 4
| r1: 8 r2: 9 r3: 9
| r1: 12 r2: 12 r3: 13
| r1: 17 r2: 17 r3: 17
| r1: 21 r2: 21 r3: 21
| r1: 25 r2: 25 r3: 25
| r1: 29 r2: 29 r3: 29
| r1: 33 r2: 34 r3: 33
| r1: 37 r2: 38 r3: 37
| r1: 41 r2: 42 r3: 41
`----


        Juergen

public class TestYield
{
    static class Runner
        implements Runnable
    {
        int delay;
        boolean justWarmUp;

        volatile int runs;

        Runner(int delay) 
        { 
            this.delay = delay;
        }
    
        Runner(int delay, boolean justWarmUp) 
        { 
            this(delay);
            this.justWarmUp = justWarmUp;
        }

        public void run() 
        {
            while (true) {
                runs++;
                for (int i = 0; i < delay; i++);
                if (justWarmUp && runs >=1) {
                    return;
                }
                Thread.yield();
            }
        }
    }
    
    public static void main(String[] args) 
        throws Throwable
    {
        int delay = 
            args.length > 0 ? Integer.parseInt(args[0]) : 100000000;
        
        Runner r1, r2, r3;
        
        if (System.getProperty("warmup") != null) {
            Thread t;
            t = new Thread(new Runner(delay, true));
            t.start();
            t.join();
            t = new Thread(new Runner(delay, true));
            t.start();
            t.join();
        }
        
        new Thread(r1 = new Runner(delay)).start();
        new Thread(r2 = new Runner(delay)).start();
        new Thread(r3 = new Runner(delay)).start();
	
        int c1, c2, c3;
        while (true) {
            Thread.sleep(2000);
            c1 = r1.runs;
            c2 = r2.runs;
            c3 = r3.runs;
            System.out.println("r1: " + c1 + " " + 
                               "r2: " + c2 + " " + 
                               "r3: " + c3);
        }
    }
}

-- 
Juergen Kreileder, Blackdown Java-Linux Team
http://www.blackdown.org/java-linux.html
JVM'01: http://www.usenix.org/events/jvm01/

Reply via email to