1. Java 的线程虽然在编程角度(API)是与平台无关的,但它的运行效果却和不同操
作系统平台密切相关。为了利用更多的 CPU 资源,Java 中的一个线程(Thread)就对应着
不同操作系统下的一个真实线程。因为 Java 虚拟机没有实现线程的调度,所以这些 Java
的线程在不同操作系统调度下运行的差异性也就比较明显。例如在 Windows 系统中,不
仅线程的优先级少于 Java API 参数规定的十个优先级,而且微软明确反对程序员动态调
整线程优先级。即使在操作系统中有足够的优先权,让线程优先级的参数和真实线程的优
先级对应,不同操作系统的调度方式也会有许多不同。这最终会造成代码在不同平台上的
行为变得不可预测。这就很难满足复杂的、大规模并发任务的众多优先级需求,从而很难
达到用户业务需要达到的效果。
2. 由于在 Java 系统中,线程被包装在一个 Java
—
语言的对象类 Thread 中,所以
为了完成 Java 语言对象和操作系统线程的对应,Java 线程的系统开销还是比较大的(在
NT 4.0 中,平均每个线程大致占用 30KB 内存)。因此如果让 Thread 对象个数和成千上
万的任务数同比例增长,就显然是不合理的。
综上所述,根据并发多任务的大规模需求和 Java 平台固有的特点,想要利用 Java
Thread 对象的优先级调整 CPU 资源的分配是非常困难的,所以应该尽量避免让线程和
任务直接对应,也尽量避免使用操作系统线程优先级的调度机制。
解决方案
根据以上分析,问题的症结在于:多任务系统中的任务在 Java 语言中的对应以及任
务间的相互调度。
从本质上看,一个任务就是一系列对象方法的调用序列,与 Java 的 Thread 对象或
者别的类的对象没有必然联系。在避免使用不同操作系统线程调度且同时 Java 虚拟机又
没有线程调度能力的情况下,要想构造一个协调式多任务系统,让各个任务相互配合就
成了最直接的思路。协调式多任务系统一般有以下特点:
1. 任务由消息驱动,消息的响应代码完成任务逻辑的处理;
2. 消息队列完成消息的存储和管理,从而利用消息处理的次序体现任务优先级的不
同;
3. 任务中耗时的消息响应逻辑能够主动放弃 CPU 资源,让别的任务执行(像
Windows 3.1 中的 Yield 函数、Visual Basic 中的 DoEvents 语句)。
可能出于巧合,Java 语言具有构造协调式多任务系统天然的条件。Java 对象的方法
不仅是一个函数调用,它还是一个 java.lang.reflect.Method 类的对象。而所有对象的
方法都可以通过 Method 类的 invoke 方法调用。如果能使每个任务所对应的一系列方法
全部以对象形式包装成消息,放到消息队列中,然后再按照自己的优先级算法将队列中
的消息取出,执行其 Method 对象的 invoke 调用,那么一个基本的协调式多任务系统
就形成了。其中,任务的优先级和线程的优先级没有绑定关系。该系统的主体调度函数可
“
”
以设置成一个 死循环 ,按照需要的优先级算法处理消息队列。对于有多重循环、外设等
待等耗时操作的消息响应函数,可以在响应函数内部递归调用主体调度函数 ,这一次调用
“
”
把原来的 死循环 改成在消息队列长度减少到一定程度(或者为空)后退出。退出后,函数
返回,执行刚才没有完成的消息响应逻辑,这样就非常自然地实现了协调式系统中任务
主动放弃 CPU 资源的要求。
如果仅仅做到这一步,完成一个像 Windows 3.1 中的多任务系统,实际只用了一个
线程,没有利用 Java 多线程的特点。应该注意到,虽然 Java 系统中线程调度与平台相关,
但是相同优先级的线程之间分时运行的特点基本上是不受特定平台影响的。各个相同优先
级的线程共享 CPU 资源,而线程又被映射成了 Java 语言中的 Thread 对象。这些对象就
可以被认为是 CPU 资源的代表。Thread
—
与线程执行代码主体的接口 Runnable 之间是