java

位置:IT落伍者 >> java >> 浏览文章

Java线程模型缺陷研究[3]


发布日期:2023年05月18日
 
Java线程模型缺陷研究[3]

注意异步方法并没有指定返回值因为其句柄将被立即返回而不用等到请求的操作处理完成后所以此时没有合理的返回值对于派生出的模型$task 关键字和 class 一样同效 $task 可以实现接口继承类和继承的其它任务标有 asynchronous 关键字的方法由 $task 在后台处理其它的方法将同步运行就像在类中一样

$task关键字可以用一个可选的 $error 从句修饰 (如上所示)它表明对任何无法被异步方法本身捕捉的异常将有一个缺省的处理程序我使用 $ 来代表被抛出的异常对象如果没有指定 $error 从句就将打印出一个合理的出错信息(很可能是堆栈跟蹤信息)

注意为确保线程安全异步方法的参数必须是不变 (immutable) 的运行时系统应通过相关语义来保证这种不变性(简单的复制通常是不够的)

所有的 task 对象必须支持一些伪信息 (pseudomessage)

除了常用的修饰符(public 等)task 关键字还应接受一个 $pooled(n) 修饰符它导致 task 使用一个线程池而不是使用单个线程来运行异步请求 n 指定了所需线程池的大小必要时此线程池可以增加但是当不再需要线程时它应该缩到原来的大小伪域 (pseudofield) $pool_size 返回在 $pooled(n) 中指定的原始 n 参数值

在《Taming Java Threads 》的第八章中我给出了一个服务器端的 socket 处理程序作为线程池的例子它是关于使用线程池的任务的一个好例子其基本思路是产生一个独立对象它的任务是监控一个服务器端的 socket每当一个客户机连接到服务器时服务器端的对象会从池中抓取一个预先创建的睡眠线程并把此线程设置为服务于客户端连接socket 服务器会产出一个额外的客户服务线程但是当连接关闭时这些额外的线程将被删除

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 的语法使它支持多个参数和能接受一个超时说明(在下面的括弧中指定)下面是我希望的语法

TimeoutException是 RuntimeException 派生类它在等待超时后即被抛出

超时是需要的但还不足以使代码强壮您还需要具备从外部中止请求锁等待的能力所以当向一个等待锁的线程传送一个interrupt() 方法后此方法应抛出一个 SynchronizationException 对象并中断等待的线程这个异常应是 RuntimeException 的一个派生类这样不必特别处理它

对synchronized 语法这些推荐的更改方法的主要问题是它们需要在二进制代码级上修改而目前这些代码使用进入监控(entermonitor)和退出监控(exitmonitor)指令来实现 synchronized 而这些指令没有参数所以需要扩展二进制代码的定义以支持多个锁定请求但是这种修改不会比在 Java 中修改 Java 虚拟机的更轻松但它是向下兼容现存的 Java 代码

另一个可解决的问题是最常见的死锁情况在这种情况下两个线程都在等待对方完成某个操作

[] [] [] [] [] [] []

               

上一篇:Java线程模型缺陷研究[4]

下一篇:Java线程模型缺陷研究[2]