>>>>> "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/