Java线程模子缺陷研究
副标题#e#
Java 编程语言的线程模子大概是此语言中最单薄的部门。它完全不适合实际巨大措施的要求,并且也完全不是面向工具的。本文发起对 Java 语言举办重大修改和增补,以办理这些问题。
Java 语言的线程模子是此语言的一个最难另人满足的部门。尽量 Java 语言自己就支持线程编程是件功德,可是它对线程的语法和类包的支持太少,只能合用于极小型的应用情况。
关于 Java 线程编程的大大都书籍都长篇累牍地指出了 Java 线程模子的缺陷,并提供了办理这些问题的抢救包(Band-Aid/邦迪创可贴)类库。我称这些类为抢救包,是因为它们所能办理的问题本应是由 Java 语言自己语法所包括的。从久远来看,以语法而不是类库要领,将能发生更高效的代码。这是因为编译器和 Java 虚拟器 (JVM) 能一同优化措施代码,而这些优化对付类库中的代码是很难或无法实现的。
Allen Holub 指出,在我的《Taming Java Threads 》(请参阅 参考资料 )书中以及本文中,我进一步发起对 Java 编程语言自己举办一些修改,以使得它可以或许真正办理这些线程编程的问题。本文和我这本书的主要区别是,我在撰写本文时举办了更多的思考, 所以对书中的提议加以了提高。这些发起只是实验性的 — 只是我小我私家对这些问题的想法,并且实现这些想法需要举办大量的事情以及同行们的评价。但这是究竟是一个初步,我有意为办理这些问题创立一个专门的事情组,假如您感乐趣,请发 e-mail 到 [email protected] 。一旦我真正着手举办,我就会给您发通知。
这里提出的发起长短常斗胆的。有些人发起对 Java 语言类型 (JLS)(请参阅参考资料 )举办细微和少量的修改以办理当前恍惚的 JVM 行为,可是我却想对其举办更为彻底的改造。
在实际草稿中,我的很多发起包罗为此语言引入新的要害字。固然凡是要求不要打破一个语言的现有代码是正确的,可是假如该语言的并不是要保持稳定以至于过期的话,它就必需能引入新的要害字。为了使引入的要害字与现有的标识符不发生斗嘴,颠末细心思量,我将利用一个 ($) 字符,而这个字符在现有的标识符中是犯科的。(譬喻,利用 $task,而不是 task)。此时需要编译器的呼吁行开关提供支持,能利用这些要害字的变体,而不是忽略这个美元标记。
task(任务)的观念
Java 线程模子的基础问题是它完全不是面向工具的。面向工具 (OO) 设计人员基础不按线程角度思量问题;他们思量的是同步 信息 异步 信息(同步信息被当即处理惩罚 — 直到信息处理惩罚完成才返回动静句柄;异步信息收到后将在靠山处理惩罚一段时间 — 而早在信息处理惩罚竣事前就返回动静句柄)。Java 编程语言中的 Toolkit.getImage() 要领就是异步信息的一个好例子。 getImage() 的动静句柄将被当即返回,而不必比及整个图像被靠山线程取回。
这是面向工具 (OO) 的处理惩罚要领。可是,如前所述,Java 的线程模子长短面向工具的。一个 Java 编程语言线程实际上只是一个run() 进程,它挪用了其它的进程。在这里就基础没有工具、异步或同步信息以及其它观念。
对付此问题,在我的书中深入接头过的一个办理要领是,利用一个Active_object。 active 工具是可以吸收异步请求的工具,它在吸收到请求后的一段时间内以靠山方法得以处理惩罚。在 Java 编程语言中,一个请求可被封装在一个工具中。譬喻,你可以把一个通过 Runnable 接话柄现的实例传送给此 active 工具,该接口的 run() 要领封装了需要完成的事情。该 runnable 工具被此 active 工具排入到行列中,当轮到它执行时,active 工具利用一个靠山线程来执行它。
在一个 active 工具上运行的异步信息实际上是同步的,因为它们被一个单一的处事线程按顺序从行列中取出并执行。因此,利用一个 active 工具以一种更为进程化的模子可以消除大大都的同步问题。
在某种意义上,Java 编程语言的整个 Swing/AWT 子系统是一个 active 工具。向一个 Swing 行列传送一条讯息的独一安详的途径是,挪用一个雷同SwingUtilities.invokeLater() 的要领,这样就在 Swing 事件行列上发送了一个 runnable 工具,当轮到它执行时, Swing 事件处理惩罚线程将会处理惩罚它。
那么我的第一个发起是,向 Java 编程语言中插手一个task (任务)的观念,从而将active 工具集成到语言中。( task的观念是从 Intel 的 RMX 操纵系统和 Ada 编程语言警惕过来的。大大都及时操纵系统都支持雷同的观念。)
一个任务有一个内置的 active 工具分发措施,并自动打点那些处理惩罚异步信息的全部机制。
界说一个任务和界说一个类基内情同,差异的只是需要在任务的要领前加一个asynchronous 修饰符来指示 active 工具的分派措施在靠山处理惩罚这些要领。
#p#分页标题#e#
所有的写请求都用一个dispatch() 进程挪用被放在 active-object 的输入行列中列队。在靠山处理惩罚这些异步信息时呈现的任何异常 (exception) 都由 Exception_handler 工具处理惩罚,此 Exception_handler 工具被传送到 File_io_task 的结构函数中。
这种基于类的处理惩罚要领,其主要问题是太巨大了 — 对付一个这样简朴的操纵,代码太杂了。向 Java 语言引入$task 和 $asynchronous 要害字后,就可以按下面这样重写以前的代码:
留意,异步要领并没有指定返回值,因为其句柄将被当即返回,而不消比及请求的操纵处理惩罚完成后。所以,此时没有公道的返回值。对付派生出的模子,$task 要害字和 class 一样同效: $task 可以实现接口、担任类和担任的其它任务。标有 asynchronous 要害字的要领由 $task 在靠山处理惩罚。其它的要领将同步运行,就像在类中一样。
$task要害字可以用一个可选的 $error 从句修饰 (如上所示),它表白对任何无法被异步要领自己捕获的异常将有一个缺省的处理惩罚措施。我利用 $ 来代表被抛出的异常工具。假如没有指定 $error 从句,就将打印出一个公道的堕落信息(很大概是仓库跟踪信息)。
留意,为确保线程安详,异步要领的参数必需是稳定 (immutable) 的。运行时系统应通过相关语义来担保这种稳定性(简朴的复制凡是是不足的)。
所有的 task 工具必需支持一些伪信息 (pseudo-message)。
除了常用的修饰符(public 等), task 要害字还应接管一个 $pooled(n) 修饰符,它导致 task 利用一个线程池,而不是利用单个线程来运行异步请求。 n 指定了所需线程池的巨细;须要时,此线程池可以增加,可是当不再需要线程时,它应该缩到本来的巨细。伪域 (pseudo-field) $pool_size 返回在 $pooled(n) 中指定的原始 n 参数值。
在《Taming Java Threads 》的第八章中,我给出了一个处事器端的 socket 处理惩罚措施,作为线程池的例子。它是关于利用线程池的任务的一个好例子。其根基思路是发生一个独立工具,它的任务是监控一个处事器端的 socket。每当一个客户机毗连随处事器时,处事器端的工具会从池中抓取一个预先建设的睡眠线程,并把此线程配置为处事于客户端毗连。socket 处事器会产出一个特另外客户处事线程,可是当毗连封锁时,这些特另外线程将被删除。
#p#副标题#e#
Socket_server工具利用一个独立的靠山线程处理惩罚异步的 listen() 请求,它封装 socket 的“接管”轮回。当每个客户端毗连时, listen() 请求一个 Client_handler 通过挪用 handle() 来处理惩罚请求。每个 handle() 请求在它们本身的线程中执行(因为这是一个 $pooled 任务)。
留意,每个传送到$pooled $task 的异步动静实际上都利用它们本身的线程来处理惩罚。典范环境下,由于一个 $pooled $task 用于实现一个自主操纵;所以对付办理与会见状态变量有关的潜在的同步问题,最好的办理要领是在 $asynchronous 要领中利用 this 是指向的工具的一个独占副本。这就是说,当向一个 $pooled $task 发送一个异步请求时,将执行一个 clone() 操纵,而且此要领的 this 指针会指向此克隆工具。线程之间的通信可通过对 static 区的同步会见实现。
改造synchronized
固然在大都环境下,$task 消除了同步操纵的要求,可是不是所有的多线程系统都用任务来实现。所以,还需要改造现有的线程模块。 synchronized 要害字有下列缺点: 无法指定一个超时值。 无法间断一个正在期待请求锁的线程。 无法安详地请求多个锁 。(多个锁只能以依序次得到。)
办理这些问题的步伐是:扩展synchronized 的语法,使它支持多个参数和能接管一个超时说明(在下面的括弧中指定)。下面是我但愿的语法:
synchronized(x && y && z) | 得到 x、y 和 z 工具的锁。 |
synchronized(x || y || z) | 得到 x、y 或 z 工具的锁。 |
synchronized( (x && y ) || z) | 对付前面代码的一些扩展。 |
synchronized(…)[1000] | 配置 1 秒超时以得到一个锁。 |
synchronized[1000] f(){…} | 在进入 f() 函数时得到 this 的锁,但可有 1 秒超时。 |
TimeoutException是 RuntimeException 派生类,它在期待超时后即被抛出。
#p#分页标题#e#
超时是需要的,但还不敷以使代码强壮。您还需要具备从外部中止请求锁期待的本领。所以,当向一个期待锁的线程传送一个interrupt() 要领后,此要领应抛出一个 SynchronizationException 工具,并间断期待的线程。这个异常应是 RuntimeException 的一个派生类,这样不必出格处理惩罚它。
对synchronized 语法这些推荐的变动要领的主要问题是,它们需要在二进制代码级上修改。而今朝这些代码利用进入监控(enter-monitor)和退出监控(exit-monitor)指令来实现 synchronized 。而这些指令没有参数,所以需要扩展二进制代码的界说以支持多个锁定请求。可是这种修改不会比在 Java 2 中修改 Java 虚拟机的更轻松,但它是向下兼容现存的 Java 代码。
另一个可办理的问题是最常见的死锁环境,在这种环境下,两个线程都在期待对方完成某个操纵。
设想一个线程挪用a() ,但在得到 lock1 之后在得到 lock2 之前被剥夺运行权。 第二个线程进入运行,挪用 b() ,得到了 lock2 ,可是由于第一个线程占用 lock1 ,所以它无法得到 lock1 ,所以它随后处于期待状态。此时第一个线程被叫醒,它试图得到 lock2 ,可是由于被第二个线程占据,所以无法得到。此时呈现死锁。
编译器(或虚拟机)会从头分列请求锁的顺序,使lock1 老是被首先得到,这就消除了死锁。
可是,这种要领对多线程不必然总乐成,所以得提供一些要领来自动冲破死锁。一个简朴的步伐就是在期待第二个锁时常释放已得到的锁。
假如期待锁的每个措施利用差异的超时值,就可冲破死锁而个中一个线程就可运行。我发起用以下的语法来代替前面的代码:
synchronized语句将永远期待,可是它时常会放弃已得到的锁以冲破潜在的死锁大概。在抱负环境下,每个反复期待的超时值比前一个相差一随机值。
改造wait() 和 notify()
wait()/ notify() 系统也有一些问题: 无法检测 wait() 是正常返回照旧因超时返回。 无法利用传统条件变量来实现处于一个“信号”(signaled)状态。 太容易产生嵌套的监控(monitor)锁定。
超时检测问题可以通过从头界说wait() 使它返回一个 boolean 变量 (而不是 void ) 来办理。一个 true 返回值指示一个正常返回,而 false 指示因超时返回。
基于状态的条件变量的观念是很重要的。假如此变量被配置成false 状态,那么期待的线程将要被阻断,直到此变量进入 true 状态;任何期待 true 的条件变量的期待线程会被自动释放。 (在这种环境下, wait() 挪用不会产生阻断。)。
嵌套监控锁定问题很是贫苦,我并没有简朴的办理步伐。嵌套监控锁定是一种死锁形式,当某个锁的占有线程在挂起其自身之前不释放锁时,会产生这种嵌套监控封闭。
此例中,在get() 和 put() 操纵中涉及两个锁:一个在 Stack 工具上,另一个在 LinkedList 工具上。下面我们思量当一个线程试图挪用一个空栈的 pop() 操纵时的环境。此线程得到这两个锁,然后挪用 wait() 释放 Stack 工具上 的锁,可是没有释放在 list 上的锁。假如此时第二个线程试图向仓库中压入一个工具,它会在 synchronized(list) 语句上永远挂起,并且永远不会被答允压入一个工具。由于第一个线程期待的是一个非空栈,这样就会发存亡锁。这就是说,第一个线程永远无法从 wait() 返回,因为由于它占据着锁,而导致第二个线程永远无法运行到 notify() 语句。
在这个例子中,有许多明明的步伐来办理问题:譬喻,对任何的要领都利用同步。可是在真实世界中,办理要领凡是不是这么简朴。
一个可行的要领是,在wait() 中凭据反顺序释放当前线程获取的 所有 锁,然后当期待条件满意后,从头按原始获取顺序取得它们。可是,我能想象出操作这种方法的代码对付人们来说的确无法领略,所以我认为它不是一个真正可行的要领。假如您有好的要领,请给我发 e-mail。
我也但愿能比及下述巨大条件被实现的一天。譬喻:
个中a 、 b 和 c 是任意工具。
修改Thread 类
同时支持抢占式和协作式线程的本领在某些处事器应用措施中是根基要求,尤其是在想使系统到达最高机能的环境下。我认为 Java 编程语言在简化线程模子上走得太远了,而且 Java 编程语言应支持 Posix/Solaris 的“绿色(green)线程”和“精练(lightweight)历程”观念(在“(Taming Java Threads ”第一章中接头)。 这就是说,有些 Java 虚拟机的实现(譬喻在 NT 上的 Java 虚拟机)应在其内部仿真协作式历程,其它 Java 虚拟机应仿真抢占式线程。并且向 Java 虚拟机插手这些扩展是很容易的。
#p#分页标题#e#
一个 Java 的Thread 应始终是抢占式的。这就是说,一个 Java 编程语言的线程应像 Solaris 的精练历程一样事情。 Runnable 接口可以用于界说一个 Solaris 式的“绿色线程”,此线程必须能把节制权转给运行在沟通精练历程中的其它绿色线程。
能有效地为Runnable 工具发生一个绿色线程,并把它绑定到由 Thread 工具代表的精练历程中。这种实现对付现有代码是透明的,因为它的有效性和现有的完全一样。
把Runnable 工具想成为绿色线程,利用这种要领,只需向 Thread 的结构函数通报几个 Runnable 工具,就可以扩展 Java 编程语言的现有语法,以支持在一个单一精练线程有多个绿色线程。(绿色线程之间可以彼此协作,可是它们可被运行在其它精练历程 ( Thread 工具) 上的绿色历程( Runnable 工具) 抢占。)。譬喻,下面的代码会为每个 runnable 工具建设一个绿色线程,这些绿色线程会共享由 Thread 工具代表的精练历程。
现有的包围(override)Thread 工具并实现 run() 的习惯继承有效,可是它应映射到一个被绑定到一精练历程的绿色线程。(在 Thread() 类中的缺省 run() 要了解在内部有效地建设第二个 Runnable 工具。)
线程间的协作
应在语言中插手更多的成果以支持线程间的彼此通信。今朝,PipedInputStream 和 PipedOutputStream 类可用于这个目标。可是对付大大都应用措施,它们太弱了。我发起向 Thread 类插手下列函数: 增加一个 wait_for_start() 要领,它凡是处于阻塞状态,直到一个线程的 run() 要领启动。(假如期待的线程在挪用 run 之前被释放,这没有什么问题)。用这种要领,一个线程可以建设一个或多个帮助线程,并担保在建设线程继承执行操纵之前,这些帮助线程会处于运行状态。 (向 Object 类)增加 $send (Object o) 和 Object=$receive() 要领,它们将利用一个内部阻断行列在线程之间传送工具。阻断行列应作为第一个 $send() 挪用的副产物被自动建设。 $send() 挪用会把工具插手行列。 $receive() 挪用凡是处于阻塞状态,直到有一个工具被插手行列,然后它返回此工具。这种要领中的变量应支持设定入队和出队的操纵超时本领: $send (Object o, long timeout) 和 $receive (long timeout)。
对付读写锁的内部支持
读写锁的观念应内置到 Java 编程语言中。读写器锁在“Taming Java Threads ”(和其它处所)中有具体接头,归纳综合地说:一个读写锁支持多个线程同时会见一个工具,可是在同一时刻只有一个线程可以修改此工具,而且在会见举办时不能修改。
对付一个工具,应该只有在$writing 块中没有线程时,才支持多个线程进入 $reading 块。在举办读操纵时,一个试图进入 $writing 块的线程会被阻断,直到读线程退出 $reading 块。 当有其它线程处于 $writing 块时,试图进入 $reading 或 $writing 块的线程会被阻断,直到此写线程退出 $writing 块。
假如读和写线程都在期待,缺省环境下,读线程会首先举办。可是,可以利用$writer_priority 属性修改类的界说来改变这种缺省方法。
会见部门建设的工具应是犯科的
当前环境下,JLS 答允会见部门建设的工具。譬喻,在一个结构函数中建设的线程可以会见正被建设的工具,既使此工具没有完全被建设。
配置x 为 -1 的线程可以和配置 x 为 0 的线程同时举办。所以,此时 x 的值无法预测。
对此问题的一个办理要领是,在结构函数没有返回之前,对付在此结构函数中建设的线程,既使它的优先级比挪用new 的线程高,也要克制运行它的 run() 要领。
这就是说,在结构函数返回之前,start() 请求必需被推迟。
别的,Java 编程语言应可答允结构函数的同步。换句话说,下面的代码(在当前环境下是犯科的)会象预期的那样事情:
我认为第一种要领比第二种更简捷,但实现起来更为坚苦。
volatile要害字应象预期的那样事情
JLS 要求保存对付 volatile 操纵的请求。大大都 Java 虚拟机都简朴地忽略了这部门内容,这是不该该的。在多处理惩罚器的环境下,很多主机都呈现了这种问题,可是它本应由 JLS 加以办理的。假如您对这方面感乐趣,马里兰大学的 Bill Pugh 正在致力于这项事情(请参阅参考资料 )。
会见的问题
假如缺少精采的会见节制,会使线程编程很是坚苦。大大都环境下,假如能担保线程只从同步子系统中挪用,不必思量线程安详(threadsafe)问题。我发起对 Java 编程语言的会见权限观念做如下限制;应准确利用 package 要害字来限制包会见权。我认为当缺省行为的存在是任何一种计较机语言的一个瑕疵,我对此刻存在这种缺省权限感想很疑惑(并且这种缺省是“包(package)”级此外而不是“私有(private)”)。在其它方面,Java 编程语言都不提供等同的缺省要害字。固然利用显式的 package 的限定词会粉碎现有代码,可是它将使代码的可读性更强,并能消除整个类的潜在错误 (譬喻,假如会见权是由于错误被忽略,而不是被存心忽略)。 从头引入 private protected ,它的成果应和此刻的 protected 一样,可是不该答允包级此外会见。 答允 private private 语法指定“实现的会见”对付所有外部工具是私有的,甚至是当前工具是的同一个类的。对付“.”左边的独一引用(隐式或显式)应是 this 。 扩展 public 的语法,以授权它可拟定特定类的会见。譬喻,下面的代码应答允 Fred 类的工具可挪用 some_method() ,可是对其它类的工具,这个要领应是私有的。
#p#分页标题#e#
这种发起差异于 C++ 的 "friend" 机制。 在 "friend" 机制中,它授权一个类会见另一个类的所有 私有部门。在这里,我发起对有限的要领荟萃举办严格节制的会见。用这种要领,一个类可觉得另一个类界说一个接口,而这个接口对系统的其余类是不行见的。
除非域引用的是真正稳定(immutable)的工具或static final 根基范例,不然所有域的界说应是 private 。对付一个类中域的直接会见违反了 OO 设计的两个根基法则:抽象和封装。从线程的概念来看,答允直接会见域只使对它举办非同步会见更容易一些。
增加$property 要害字。带有此要害字的工具可被一个“bean 盒”应用措施会见,这个措施利用在 Class 类中界说的反射操纵(introspection) API,不然与 private private 同效。 $property 属性可用在域和要领,这样现有的 JavaBean getter/setter 要领可以很容易地被界说为属性。
稳定性(immutability)
由于对稳定工具的会见不需要同步,所以在多线程条件下,稳定的观念(一个工具的值在建设后不行变动)是无价的。Java 编程言语中,对付稳定性的实现不足严格,有两个原因:对付一个稳定工具,在其被未完全建设之前,可以对它举办会见。这种会见对付某些域可以发生不正确的值。 对付恒定 (类的所有域都是 final) 的界说太松散。对付由 final 引用指定的工具,固然引用自己不能改变,可是工具自己可以改变状态。
第一个问题可以办理,不答允线程在结构函数中开始执行 (可能在结构函数返回之前不能执行开始请求)。
对付第二个问题,通过限定final 修饰符指向恒定工具,可以办理此问题。这就是说,对付一个工具,只有所有的域是 final,而且所有引用的工具的域也都是 final,此工具才真正是恒定的。为了不冲破现有代码,这个界说可以利用编译器增强,即只有一个类被显式标为稳定时,此类才是稳定类。
有了$immutable 修饰符后,在域界说中的 final 修饰符是可选的。
最后,当利用内部类(inner class)后,在 Java 编译器中的一个错误使它无法靠得住地建设稳定工具。
既使空的 final 在每个结构函数中都有初始化,照旧会呈现这个错误信息。自从在 1.1 版本中引入内部类后,编译器中一直有这个错误。在此版本中(三年今后),这个错误依然存在。此刻,该是纠正这个错误的时候了。
对付类级域的实例级会见
除了会见权限外,尚有一个问题,即类级(静态)要领和实例(非静态)要领都能直接会见类级(静态)域。这种会见长短常危险的,因为实例要领的同步不会获取类级的锁,所以一个synchronized static 要领和一个 synchronized 要领照旧能同时会见类的域。纠正此问题的一个明明的要领是,要求在实例要领中只有利用 static 会见要领才气会见非稳定类的 static 域。虽然,这种要求需要编译器和运行时间查抄。
由于f() 和 g() 可以并行运行,所以它们能同时改变 x 的值(发生不定的功效)。请记着,这里有两个锁: static 要领要求属于 Class 工具的锁,而非静态要领要求属于此类实例的锁。
或则,编译器应得到读/写锁的利用:
别的一种要领是(这也是一种抱负的 要领)– 编译器应 自动 利用一个读/写锁来同步会见非稳定 static 域,这样,措施员就不必担忧这个问题。
靠山线程的溘然竣事
当所有的非靠山线程终止后,靠山线程都被溘然竣事。当靠山线程建设了一些全局资源(譬喻一个数据库毗连或一个姑且文件),尔靠山线程竣事时这些资源没有被封锁或删除就会导致问题。
#p#分页标题#e#
对付这个问题,我发起拟定法则,使 Java 虚拟机在下列环境下不封锁应用措施:有任何非靠山线程正在运行,可能: 有任何靠山线程正在执行一个 synchronized 要领或 synchronized 代码块。
靠山线程在它执行完synchronized 块或 synchronized 要领后可被当即封锁。
从头引入stop() 、 suspend() 和 resume() 要害字
由于实用原因这也许不行行,可是我但愿不要破除stop() (在 Thread 和 ThreadGroup 中)。可是,我会改变 stop() 的语义,使得挪用它时不会粉碎已有代码。可是,关于 stop() 的问题,请记着,当线程终止后, stop() 将释放所有锁,这样大概潜在地使正在此工具上事情的线程进入一种不不变(局部修改)的状态。由于遏制的线程已释放它在此工具上的所有锁,所以这些工具无法再被会见。
对付这个问题,可以从头界说stop() 的行为,使线程只有在不占有任何锁时才当即终止。假如它占据着锁,我发起在此线程释放最后一个锁后才终止它。可以利用一个和抛出异常相似的机制来实现此行为。被遏制线程应配置一个符号,而且当退出所有同步块时当即测试此符号。假如配置了此符号,就抛出一个隐式的异常,可是此异常应不再能被捕获而且当线程竣事时不会发生任何输出。留意,微软的 NT 操纵系统不能很好地处理惩罚一个外部指示的溘然遏制(abrupt)。(它不把 stop 动静通知动态毗连库,所以大概导致系统级的资源裂痕。)这就是我发起利用雷同异常的要领简朴地导致 run() 返回的原因。
与这种和异常雷同的处理惩罚要领带来的实际问题是,你必须在每个synchronized 块后都插入代码来测试“stopped”符号。而且这种附加的代码会低落系统机能并增加代码长度。我想到的别的一个步伐是使 stop() 实现一个“延迟的(lazy)”遏制,在这种环境下,在下次挪用 wait() 或 yield() 时才终止。我还想向 Thread 中插手一个 isStopped() 和 stopped() 要领(此时, Thread 将像 isInterrupted() 和 interrupted() 一样事情,可是会检测 “stop-requested”的状态)。这种要领不向第一种那样通用,可是可行而且不会发生过载。
应把suspend() 和 resume() 要领放回到 Java 编程语言中,它们是很有用的,我不想被当成是幼儿园的小孩。由于它们大概发生潜在的危险(当被挂起时,一个线程可以占据一个锁)而去掉它们是没有原理的。请让我本身来抉择是否利用它们。假如吸收的线程正占据着锁,Sun 公司应该把它们作为挪用 suspend() 的一个运行时间异常处理惩罚(run-time exception);可能更好的要领是,延迟实际的挂起进程,直到线程释放所有的锁。
被阻断的 I/O 应正确事情
应该能打断任何被阻断的操纵,而不是只让它们wait() 和 sleep() 。我在“ Taming Java Threads ”的第二章中的 socket 部门接头了此问题。可是此刻,对付一个被阻断的 socket 上的 I/O 操纵,打断它的独一步伐是封锁这个 socket,而没有步伐打断一个被阻断的文件 I/O 操纵。譬喻,一旦开始一个读请求而且进入阻断状态后,除非到它实际读出一些对象,不然线程一直出于阻断状态。既使关掉文件句柄也不能打断读操纵。
尚有,措施应支持 I/O 操纵的超时。所有大概呈现阻断操纵的工具(譬喻 InputStream 工具)也都应支持这种要领。
这和 Socket 类的setSoTimeout(time) 要领是等价的。同样地,应该支持把超时作为参数通报到阻断的挪用。
ThreadGroup类
ThreadGroup应该实现 Thread 中可以或许改变线程状态的所有要领。我出格想让它实现 join() 要领,这样我就可期待组中的所有线程的终止。
总结
以上是我的发起。就像我在标题中所说的那样,假如我是国王…(哎)。我但愿这些改变(或其它等同的要领)最终能被引入 Java 语言中。我确实认为 Java 语言是一种伟大的编程语言;可是我也认为 Java 的线程模子设计得还不足完善,这是一件很惋惜的工作。可是,Java 编程语言正在演变,所以尚有可提高的前景。