日期:2014-05-20  浏览次数:20645 次

Java面试问题之三十三
谈谈你对Java线程调度模型的理解。

答:计算机通常只有一个CPU,在任意的时刻只能执行一条机器指令,每个线程只能获得CPU的使用权才能执行指令。所谓多线程的并发运行,其实是指从宏观上看,每个线程轮流获得CPU的使用权,分别执行各自的任务。在运行池中,会有多个处于就绪状态的线程在等待CPU,Java虚拟机的一项任务就是负责线程的调度。线程的调度是指按照特定的机制为多个线程分配CPU的使用权。一般存在两种不同的调度模型,分别为:分时调度模型和抢占式调度模型。

其中,Java虚拟机采用的是抢占式调度模型,也就是说优先让高优先级的线程使用CPU,如果可运行池中线程的优先级相同,则就会随机选择一个线程让其使用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。一个线程会因为以下原因而放弃CPU:
(1)Java虚拟机让当前线程暂时放弃CPU,转到就绪状态,使其他线程获得运行机会。
(2)当前线程因为某些原因而进入阻塞状态。
(3)线程运行结束。
一个线程的调度,不仅仅与Java虚拟机有关,而且与运行Java虚拟机的操作系统有很大关系。不同的操作系统对线程的调度策略是有区别的。Java线程的调度不是分时的,同时启动多个线程,不能保证各个线程轮流获得相等的CPU使用时间。


知识扩展:如果希望明确地让一个线程给另外一个线程运行的机会,可以采取以下措施之一:
(1)调整各个线程的优先级。提高需要优先执行的线程的级别。Thread类的setPriority(int)和getPriority()方法分别用来设置优先级和读取优先级。优先级用整数表示,取值范围1~10,Thread类有三个静态常量,最高优先级(MAX_PRIORITY -- 10)、最低优先级(MIN_PRIORITY -- 1)和默认优先级(NORM_PRIORITY  -- 5)。
(2)让处于运行状态的线程调用Thread.sleep()方法,主动让出CPU的使用时间。
(3)让处于运行状态的线程调用Thread.yield()方法。
(4)让处于运行状态的线程调用另一个线程的join()方法。


另外,尽管Java提供了10个优先级,但它与多数操作系统都不能很好地映射。例如,Windows 2000中只有7个优先级,并且是不固定的,而Sun公司的Solaris操作系统总共有2的31次方个优先级。如果希望程序能够一直到不同的操作系统上还能保持线程设置的优先级不变,只能使用MAX_PRIORITY、MIN_PRIORITY和NORM_PRIORITY这三个优先级。这样才能保证在不同的操作系统中,对同样优先级的线程采用了同样的调度方式。