System Thread Priorities

Chapter 6 - Thread Prioritization

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Voluntarily Relinquishing the Processor: Thread.yield()
To help ensure that other threads in the VM get a turn to run on the processor, a thread can voluntarily give up its turn early. If a thread invokes the static method Thread.yield() , the thread scheduler will swap it off the processor and allow another thread to run. It is likely (but not guaranteed ) that only threads having a priority equal to or greater than the one that yielded control will be considered by the thread scheduler.
A thread implicitly yields the processor when it goes to sleep or otherwise blocks. The Thread.yield() method allows a thread to specify other times that are convenient for it to pause to allow other threads access to the processor. If you have a thread that frequently blocks on its own, there is no need to make Thread.yield() calls. But, if you have a thread that is performing a long non-blocking calculation, an occasional call to Thread.yield() can help split up the processor resources among the other threads. Be careful not to overuse Thread.yield() as some system overhead is incurred to perform a context switch between threads. As a rough guideline, try to avoid calling Thread.yield() more than five times per second.
The PriorityCompete class shown in Listing 6.5 creates three threads that all compete with each other to run nonblocking code. It runs once without using Thread.yield() and then a second time with voluntary yielding.
Listing 6.5  PriorityCompete.javaCompeting Threads of Different Priorities
  1: public class PriorityCompete extends Object {
  2:     private volatile int count;
  3:     private boolean yield;
  4:     private Thread internalThread;
  5:     private volatile boolean noStopRequested;
  6:
  7:     public PriorityCompete(
  8:                 String name ,
  9:                 int priority,
10:                 boolean yield
11:            ) {
12:
13:         count = 0;
14:         this.yield = yield;
15:
16:         noStopRequested = true;
17:         Runnable r = new Runnable() {
18:                 public void run() {
19:                     try {
20:                         runWork();
21:                     } catch (Exception x) {
22:                         // in case ANY exception slips through
23:                         x.printStackTrace();
24:                     }
25:                 }
26:             };
27:
28:         internalThread = new Thread(r, name);
29:         internalThread.setPriority(priority);
30:     }
31:
32:     private void runWork() {
33:         Thread.yield();
34:
35:         while (noStopRequested) {
36:             if (yield) {
37:                 Thread.yield();
38:             }
39:
40:             count++;
41:
42:             for (int i = 0; i < 1000; i++) {
43:                 double x = i * Math.PI / Math.E;
44:             }
45:         }
46:     }
47:
48:     public void startRequest() {
49:         internalThread.start();
50:     }
51:
52:     public void stopRequest() {
53:         noStopRequested = false;
54:     }
55:
56:     public int getCount() {
57:         return count;
58:     }
59:
60:     public String getNameAndPriority() {
61:         return internalThread.getName() +
62:             : priority= + internalThread.getPriority();
63:     }
64:
65:     private static void runSet(boolean yield) {
66:         PriorityCompete[] pc = new PriorityCompete[3];
67:         pc[0] = new PriorityCompete(PC0, 3, yield);
68:         pc[1] = new PriorityCompete(PC1, 6, yield);
69:         pc[2] = new PriorityCompete(PC2, 6, yield);
70:
71:         // let the dust settle for a bit before starting them up
72:         try { Thread.sleep(1000); }
73:         catch (InterruptedException x) { }
74:
75:         for (int i = 0; i < pc.length; i++) {
76:             pc[i].startRequest();
77:         }
78:
79:         long startTime = System.currentTimeMillis();
80:         try { Thread.sleep(10000); }
81:         catch (InterruptedException x) { }
82:
83:         for (int i = 0; i < pc.length; i++) {
84:             pc[i].stopRequest();
85:         }
86:
87:         long stopTime = System.currentTimeMillis();
88:
89:         // let things settle down again
90:         try { Thread.sleep(1000); }
91:         catch (InterruptedException x) { }
92:
93:         int totalCount = 0;
94:         for (int i = 0; i < pc.length; i++) {
95:             totalCount += pc[i].getCount();
96:         }
97:
98:         System.out.println(totalCount= + totalCount +
99:             , count/ms= + roundTo(((double) totalCount) /
100:                                     (stopTime - startTime), 3));
101:
102:         for (int i = 0; i < pc.length; i++) {
103:             double perc = roundTo(100.0 * pc[i].getCount() /
104:                                     totalCount, 2);
105:             System.out.println(pc[i].getNameAndPriority() +
106:                 , + perc + %, count= + pc[i].getCount());
107:         }
108:     }
109:
110:     public static double roundTo(double val, int places) {
111:         double factor = Math.pow(10, places);
112:         return ((int) ((val * factor) + 0.5)) / factor;
113:     }
114:
115:     public static void main(String[] args) {
116:         Runnable r = new Runnable() {
117:                 public void run() {
118:                     System.out.println(
119:                             Run without using yield());
120:                     System.out.println(
121:                             =========================);
122:                     runSet(false);
123:
124:                     System.out.println();
125:                     System.out.println(Run using yield());
126:                     System.out.println(=================);
127:                     runSet(true);
128:                 }
129:             };
130:
131:         Thread t = new Thread(r, PriorityCompete);
132:        t.setPriority(Thread.MAX_PRIORITY - 1);
133:         t.start();
134:     }
135: }
In the main() method of PriorityCompete (lines 115134), a new thread is created to run at a higher priority than the main thread normally does. This thread is named PriorityCompete (line 131) and runs at almost the maximum priority allowed (line 132). When it is started (line 133), the run() method (lines 117128) is executed by the PriorityCompete thread. First it calls runSet() , passing false to indicate that the threads should not voluntarily yield to one another (line 122). After that completes, it calls runSet() again, this time passing in true to indicate that the threads should yield (line 127).
Each time the runSet() method (lines 65108) is called, it creates three instances of PriorityCompete (lines 6669). Two of them will run at a priority level of 6 and the third will run at a lower priority level of 3 . The PriorityCompete thread then sleeps for a second (line 72) to allow the VM to settle down (making sure that the main thread has had an opportunity to leave main() , among other things). It starts each of the objects running (lines 7577). Next , it takes note of the time (line 79), and then lets the objects compete for the processor for 10 seconds (line 80). After running for 10 seconds, they are stopped (lines 8385), and the time is noted again (line 87). The time is requested because the sleep is usually a little longer than 10 seconds because of all the other processing activity.
Each of the PriorityCompete objects increments its internal counter. The final count that each achieved is retrieved and totaled (lines 9396). The total count and the count increments per millisecond are printed (lines 98100).
Finally, the count for each PriorityCompete object is printed along with the percent of the total that its count represents (lines 102107). The roundTo() method (lines 110113) is simply used to round a double to the specified number of decimal places.
Each instance of PriorityCompete creates its own internal thread that will run at the specified priority (lines 730). PriorityCompete is roughly based on the self-running object technique shown in Chapter 11, Self-Running Objects. The internal thread is created (line 28) and has its priority set (line 29), but is not started until startRequest() (lines 4850) is called. The stopRequest() method (lines 5254) simply changes an indicator variable so that the next time the internal thread checks, it returns from run() . The getCount() method (lines 5658) and the getNameAndPriority() method (lines 6063) are used in runSet() to retrieve information.
Each of the internal threads ends up invoking its own runWork() method (lines 3246). Right away, it yields to allow all the threads to get a chance to start running (line 33). The while loop (lines 3545) is executed until a stop request is received. Each time though the loop, if this instance is supposed to yield, it executes Thread.yield() (lines 3638). Then it increments the internal counter (line 40). To slow down the progress through the loop, some busy work is done (lines 4244). This busy work does not block, but keeps the processor working.
Listing 6.6 shows possible output when PriorityCompete is run. Your output is likely to differ significantly, as it is heavily dependent on how powerful the hardware is and how many other processes are running on the operating system.
Listing 6.6  Possible Output from PriorityCompete
1: Run without using yield()
2: =========================
3: totalCount=764469, count/ms=76.523
4: PC0: priority=3, 4.65%, count=35510
5: PC1: priority=6, 48.19%, count=368395
6: PC2: priority=6, 47.17%, count=360564
7:
8: Run using yield()
9: =================
10: totalCount=253523, count/ms=25.352
11: PC0: priority=3, 0.01%, count=31
12: PC1: priority=6, 49.99%, count=126739
13: PC2: priority=6, 50.0%, count=126753
In the first set (lines 16), the internal threads never block and only reluctantly relinquish the processor when the thread scheduler forces them into the ready-to-run state. Even though there is always a thread with a priority of 6 ready-to-run, the thread scheduler still gives the thread with a priority of 3 a chance to run now and thenjust less than 5% of the time (line 4). Otherwise, the remaining percentage is roughly split between the two priority 6 threads (lines 56).
In the second set (lines 813), the internal threads yield each time though the loop. In this case, the lower-priority thread gets much less time (but not zero time) on the processor and gets only 0.01% of the processor resources. The high-priority threads split the time almost perfectly in half between themselves : 49.99% and 50.00%.
Also note that without yielding, the count/ms was about 76 (line 3), and when yielding was done, it was cut down to about 25 (line 10). This shows that the yielding caused a lot of context switches that brought with them some significant overhead. In this example, the excessive yielding wasted processor resources.

Toc


Java Thread Programming
Java Thread Programming
ISBN: 0672315858
EAN: 2147483647
Year: 2005
Pages: 149
Authors: Paul Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net