Thread Priorities
In the Java programming language, every thread has a priority. By default, a thread inherits the priority of its parent thread. You can increase or decrease the priority of any thread with the setPriority method. You can set the priority to any value between MIN_PRIORITY (defined as 1 in the Thread class) and MAX_PRIORITY (defined as 10). NORM_PRIORITY is defined as 5.
Whenever the thread-scheduler has a chance to pick a new thread, it generally picks the highest-priority thread that is currently runnable.
CAUTION
We need to say right at the outset that the rules for thread priorities are highly system-dependent. When the virtual machine relies on the thread implementation of the host platform, the thread scheduling is at the mercy of that thread implementation. The virtual machine maps the thread priorities to the priority levels of the host platform (which may have more or fewer thread levels). What we describe in this section is an ideal situation that every virtual machine implementation tries to approximate to some degree.
The highest-priority runnable thread keeps running until:
It yields by calling the yield method, or
It ceases to be runnable (either by dying or by entering the blocked state), or
A higher-priority thread has become runnable (because the higher-priority thread has slept long enough, or its I/O operation is complete, or someone unblocked it by calling the notifyAll/notify method on the object that the thread was waiting for).
Then, the scheduler selects a new thread to run. The highest-priority remaining thread is picked among those that are runnable.
What happens if there is more than one runnable thread with the same (highest) priority? One of the highest priority threads gets picked. It is completely up to the thread scheduler how to arbitrate between threads of the same priority. The Java programming language gives no guarantee that all of the threads get treated fairly. Of course, it would be desirable if all threads of the same priority are served in turn, to guarantee that each of them has a chance to make progress. But it is at least theoretically possible that on some platforms a thread scheduler picks a random thread or keeps picking the first available thread. This is a weakness of the Java programming language, and it is difficult to write multithreaded programs that are guaranteed to work identically on all virtual machines.
CAUTION
Some platforms (such as Windows NT) have fewer priority levels than the 10 levels that the Java platform specifies. On those platforms, no matter what mapping of priority levels is chosen, some of the 10 JVM levels will be mapped to the same platform levels. In the Sun JVM for Linux, thread priorities are ignored altogether, so you will not be able to see the "express threads" in action when you run the sample program at the end of this section.
CAUTION
Whenever the host platform uses fewer priority levels than the Java platform, a thread can be preempted by another thread with a seemingly lower priority. That plainly means that you cannot rely on priority levels in your multithreaded programs.
Consider, for example, a call to yield. It may have no effect on some implementations. The levels of all runnable threads might map to the same host thread level. The host scheduler might make no effort towards fairness and might simply keep reactivating the yielding thread, even though other threads would like to get their turn. It is a good idea to call sleep insteadat least you know that the current thread won't be picked again right away.
Most virtual machines have several (although not necessarily 10) priorities, and thread schedulers make some effort to rotate among equal priority threads. For example, if you watch a number of ball threads in the preceding example program, all balls progressed to the end and they appeared to get executed at approximately the same rate. Of course, that is not a guarantee. If you need to ensure a fair scheduling policy, you must implement it yourself.
Consider the following example program, which modifies the previous program to run threads of one kind of balls (displayed in red) with a higher priority than the other threads. (The bold line in the code below shows how to increase the priority of the thread.)
If you click on the "Start" button, a thread is launched at the normal priority, animating a black ball. If you click on the "Express" button, then you launch a red ball whose thread runs at a higher priority than the regular ball threads.
public class BounceFrame { public BounceFrame() { . . . addButton(buttonPanel, "Start", new ActionListener() { public void actionPerformed(ActionEvent evt) { addBall(Thread.NORM_PRIORITY, Color.black); } }); addButton(buttonPanel, "Express", new ActionListener() { public void actionPerformed(ActionEvent evt) { addBall(Thread.NORM_PRIORITY + 2, Color.red); } }); . . . } public void addBall(int priority, Color color) { Ball b = new Ball(canvas, color); canvas.add(b); BallThread thread = new BallThread(b); thread.setPriority(priority); thread.start(); } . . . }
Try it out. Launch a set of regular balls and a set of express balls. You will notice that the express balls seem to run faster. This is solely a result of their higher priority, not because the red balls run at a higher speed. The code to move the express balls is the same as that of the regular balls.
Here is why this demonstration works: five milliseconds after an express thread goes to sleep, the scheduler wakes it. Now:
The scheduler again evaluates the priorities of all the runnable threads;
It finds that the express threads have the highest priority.
One of the express threads gets another turn right away. This can be the one that just woke up, or perhaps it is another express threadyou have no way of knowing. The express threads take turns, and only when they are all asleep does the scheduler give the lower-priority threads a chance to run. See Figure 16 and Example 13.
Note that the lower-priority threads would have no chance to run if the express threads had called yield instead of sleep.
Once again, we caution you that this program works fine on Windows NT and Solaris, but the Java programming language specification gives no guarantee that it works identically on other implementations.
TIP
If you find yourself tinkering with priorities to make your code work, you are on the wrong track. Instead, read the remainder of this chapter, or delve into one of the cited references, to learn more about reliable mechanisms for controlling multithreaded programs.
Figure 16: Threads with different priorities
Example 13: BounceExpress.java
1. import java.awt.*; 2. import java.awt.event.*; 3. import java.awt.geom.*; 4. import java.util.*; 5. import javax.swing.*; 6. 7. /** 8. Shows animated bouncing balls, some running in higher priority 9. threads 10. */ 11. public class BounceExpress 12. { 13. public static void main(String[] args) 14. { 15. JFrame frame = new BounceFrame(); 16. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 17. frame.show(); 18. } 19. } 20. 21. /** 22. The frame with canvas and buttons. 23. */ 24. class BounceFrame extends JFrame 25. { 26. /** 27. Constructs the frame with the canvas for showing the 28. bouncing ball and Start and Close buttons 29. */ 30. public BounceFrame() 31. { 32. setSize(WIDTH, HEIGHT); 33. setTitle("BounceExpress"); 34. 35. Container contentPane = getContentPane(); 36. canvas = new BallCanvas(); 37. contentPane.add(canvas, BorderLayout.CENTER); 38. JPanel buttonPanel = new JPanel(); 39. addButton(buttonPanel, "Start", 40. new ActionListener() 41. { 42. public void actionPerformed(ActionEvent evt) 43. { 44. addBall(Thread.NORM_PRIORITY, Color.black); 45. } 46. }); 47. 48. addButton(buttonPanel, "Express", 49. new ActionListener() 50. { 51. public void actionPerformed(ActionEvent evt) 52. { 53. addBall(Thread.NORM_PRIORITY + 2, Color.red); 54. } 55. }); 56. 57. addButton(buttonPanel, "Close", 58. new ActionListener() 59. { 60. public void actionPerformed(ActionEvent evt) 61. { 62. System.exit(0); 63. } 64. }); 65. contentPane.add(buttonPanel, BorderLayout.SOUTH); 66. } 67. 68. /** 69. Adds a button to a container. 70. @param c the container 71. @param title the button title 72. @param listener the action listener for the button 73. */ 74. public void addButton(Container c, String title, 75. ActionListener listener) 76. { 77. JButton button = new JButton(title); 78. c.add(button); 79. button.addActionListener(listener); 80. } 81. 82. /** 83. Adds a bouncing ball to the canvas and starts a thread 84. to make it bounce 85. @param priority the priority for the threads 86. @color the color for the balls 87. */ 88. public void addBall(int priority, Color color) 89. { 90. Ball b = new Ball(canvas, color); 91. canvas.add(b); 92. BallThread thread = new BallThread(b); 93. thread.setPriority(priority); 94. thread.start(); 95. } 96. 97. private BallCanvas canvas; 98. public static final int WIDTH = 450; 99. public static final int HEIGHT = 350; 100. } 101. 102. /** 103. A thread that animates a bouncing ball. 104. */ 105. class BallThread extends Thread 106. { 107. /** 108. Constructs the thread. 109. @aBall the ball to bounce 110. */ 111. public BallThread(Ball aBall) { b = aBall; } 112. 113. public void run() 114. { 115. try 116. { 117. for (int i = 1; i <= 1000; i++) 118. { 119. b.move(); 120. sleep(5); 121. } 122. } 123. catch (InterruptedException exception) 124. { 125. } 126. } 127. 128. private Ball b; 129. } 130. 131. /** 132. The canvas that draws the balls. 133. */ 134. class BallCanvas extends JPanel 135. { 136. /** 137. Add a ball to the canvas. 138. @param b the ball to add 139. */ 140. public void add(Ball b) 141. { 142. balls.add(b); 143. } 144. 145. public void paintComponent(Graphics g) 146. { 147. super.paintComponent(g); 148. Graphics2D g2 = (Graphics2D)g; 149. for (int i = 0; i < balls.size(); i++) 150. { 151. Ball b = (Ball)balls.get(i); 152. b.draw(g2); 153. } 154. } 155. 156. private ArrayList balls = new ArrayList(); 157. } 158. 159. /** 160. A ball that moves and bounces off the edges of a 161. component 162. */ 163. class Ball 164. { 165. /** 166. Constructs a ball in the upper left corner 167. @c the component in which the ball bounces 168. @aColor the color of the ball 169. */ 170. public Ball(Component c, Color aColor) 171. { 172. canvas = c; 173. color = aColor; 174. } 175. 176. /** 177. Draws the ball at its current position 178. @param g2 the graphics context 179. */ 180. public void draw(Graphics2D g2) 181. { 182. g2.setColor(color); 183. g2.fill(new Ellipse2D.Double(x, y, XSIZE, YSIZE)); 184. } 185. 186. /** 187. Moves the ball to the next position, reversing direction 188. if it hits one of the edges 189. */ 190. public void move() 191. { 192. x += dx; 193. y += dy; 194. if (x < 0) 195. { 196. x = 0; 197. dx = -dx; 198. } 199. if (x + XSIZE >= canvas.getWidth()) 200. { 201. x = canvas.getWidth() - XSIZE; 202. dx = -dx; 203. } 204. if (y < 0) 205. { 206. y = 0; 207. dy = -dy; 208. } 209. if (y + YSIZE >= canvas.getHeight()) 210. { 211. y = canvas.getHeight() - YSIZE; 212. dy = -dy; 213. } 214. 215. canvas.repaint(); 216. } 217. 218. private Component canvas; 219. private Color color; 220. private static final int XSIZE = 15; 221. private static final int YSIZE = 15; 222. private int x = 0; 223. private int y = 0; 224. private int dx = 2; 225. private int dy = 2; 226. }
java.lang.Thread
void setPriority(int newPriority)
sets the priority of this thread. The priority must be between Thread.MIN_PRIORITY and Thread.MAX_PRIORITY. Use Thread.NORM_PRIORITY for normal priority.static int MIN_PRIORITY
is the minimum priority that a Thread can have. The minimum priority value is 1.static int NORM_PRIORITY
is the default priority of a Thread. The default priority is 5.static int MAX_PRIORITY
is the maximum priority that a Thread can have. The maximum priority value is 10.static void yield()
causes the currently executing thread to yield. If there are other runnable threads whose priority is at least as high as the priority of this thread, they will be scheduled next. Note that this is a static method.