线程的优先级
每个线程都具有各自的优先级,线程的优先级可以表明该线程在程序中的重要性,如果有很多线程处于就绪状态,那么系统会根据优先级来决定首先使哪个线程进入运行状态。但这并不意味着低优先级的线程得不到运行,而只是它运行的概率比较小,如垃圾回收线程的优先级就较低。
Thread 类中包含的成员变量代表了线程的某些优先级,如 Thread.MIN_PRIORITY(常数1)、Thread.MAX_PRIORITY(常数10)、Thread.NORM_PRIORITY(常数5)。其中,每个线程的优先级都在 Thread.MIN_PRIORITY~Thread.MAX_PRIORITY,在默认情况下其优先级都是 Thread.NORM_PRIORITY。每个新产生的线程都继承了父线程的优先级。
在多任务操作系统中,每个线程都会得到一小段 CPU 时间片运行,在时间结束时,将轮换另一个线程进入运行状态,这时系统会选择与当前线程优先级相同的线程予以运行。系统始终选择就绪状态下优先级较高的线程进入运行状态。处于各个优先级状态下的线程的运行顺序如图20.8所示。
在图20.8中:优先级为5的线程A首先得到 CPU 时间片;当该时间结束后,轮换到与线程A相同优先级的线程B;当线程B的运行时间结束后,会继续轮换到线程A,直到线程A与线程B都执行完毕,才会轮换到线程C;当线程C结束后,才会轮换到线程D。
线程的优先级可以使用 setPriority() 方法进行调整,如果使用该方法设置的优先级不是 1~10,则将产生 IllegalArgumentException 异常。

【例20.6】观察将不同优先级的线程执行完毕的顺序(实例位置:资源包\TM\sl\20\6)
创建 PriorityTest 类并实现 Runnable 接口,在 run() 方法中执行5万次字符串拼接。在主方法中以 PriorityTest 对象为参数创建4个线程,并分配不同的优先级,然后启动这些线程。
public class PriorityTest implements Runnable {
String name;
public PriorityTest(String name) {
this.name = name;
}
@Override
public void run() {
String tmp = "";
for (int i = 0; i < 50000; i++) {// 完成五万次字符串拼接
tmp += i;
}
System.out.println(name + "线程完成任务");
}
public static void main(String[] args) {
Thread a = new Thread(new PriorityTest("A"));
a.setPriority(1);// A线程优先级最小
Thread b = new Thread(new PriorityTest("B"));
b.setPriority(3);
Thread c = new Thread(new PriorityTest("C"));
c.setPriority(7);
Thread d = new Thread(new PriorityTest("D"));
d.setPriority(10);// D线程优先级最大
a.start();
b.start();
c.start();
d.start();
}
}
由于线程的执行顺序是由 CPU 决定的,即使线程设定了优先级也是作为 CPU 的参考数据,因此真实的运行结果可能并不一定按照优先级排序,例如笔者运行的结果如下:
D线程完成任务
B线程完成任务
C线程完成任务
A线程完成任务
从这个结果中可以看出,优先级最大的D线程是第一个完成的,优先级最小的A线程是最后一个完成的。但是,C线程的优先级比B线程大,却仍然在B线程之后才完成,这是 CPU 的真实运行结果。
编程训练(答案位置:资源包\TM\sl\20\编程训练)
【训练5】让兔子跑更快 创建乌龟和兔子两个线程类,让兔子线程的默认优先级为10,乌龟线程的默认优先级为1。让乌龟先启动,看谁先完成。
【训练6】客车售票程序 编写一个客车售票程序,共有10万张票,用不同线程来购票,但军人优先级最高,其次是老年人,再次是儿童,普通成人优先级最低。