诊断Java代码:孤线程(Orphaned Thread)错误模式

在多线程代码中,使用驱动其它线程所负责的动作的单个主线程是常见的。这个主线程发送消息,通常是通过把它们放到一个队列中,然后其它线程处理这些消息。但是如果主线程抛出一个异常,那么剩余的线程会继续运行,等待更多输入到该队列,导致程序冻结。在诊断 Java 代码的这一部分中,专职 Java 开发者兼兼职捉虫者 Eric Allen 讨论检测、修复和避免这一错误模式。

用多线程编写代码对程序员大有好处。多线程能使编程(和程序)进行得快得多,而且代码能有效得多地使用资源。然而,跟生活中的很多事情一样,多线程也存在缺点。因为多线程代码天生是非确定性的,出现错误的可能性大得多。而且,确实发生的的错误很难重现,因此也更难解决。

孤线程模式

Java 编程语言为多线程代码提供了丰富的支持,包括一项特别有用的功能:能够在一个线程中抛出一个异常而不影响其它线程。但这项功能会导致很多难以跟踪的错误。

从某个线程的崩溃中恢复过来是有意义,在此种情况下,这种能力能增加程序的健壮性级别。然而,它也使我们难以判断这些线程之一在什么时候抛出了一个异常。因为剩余的线程将继续运行,所以程序会表现出无响应或冻结程序的征兆。对线程之间频繁通信的程序而言尤其如此。

考虑清单 1 所示的示例,其中的一对线程通过生产者-消费者模型进行通信。

清单 1. 一个简单的、多线程的消费者-生产者程序

public class Server extends Thread {
  Client client;
  int counter;
  public Server(Client _client) {
   this.client = _client;
   this.counter = 0;
  }
  public void run() {
   while (counter < 10) {
    this.client.queue.addElement(new Integer(counter));
    counter++;
   }
   throw new RuntimeException("counter >= 10");
  }
  public static void main(String[] args) {
   Client c = new Client();
   Server s = new Server(c);
   c.start();
   s.start();
  }
}
class Client extends Thread {
  Vector queue;
  public Client() {
   this.queue = new Vector();
  }
  public void run() {
   while (true) {
    if (! (queue.size() == 0)) {
     processNextElement();
    }
   }
  }
  private void processNextElement() {
   Object next = queue.elementAt(0);
   queue.removeElementAt(0);
   System.out.println(next);
  }
}

时间: 2025-01-18 01:08:12

诊断Java代码:孤线程(Orphaned Thread)错误模式的相关文章

诊断Java代码: Impostor Type错误模式

当使用字段中特殊的标记来区别对象类型时,可能会产生标记对相关数据误贴标签的错误 ― 通称为 Impostor Type 错误模式.在诊断 Java 代码的这一部分中,Eric Allen 对这个错误的症状和起因进行了分析,详细说明了预防错误发生的方法,并讨论了一种吸引人的混合实现方法,这种方法不使用 impostor type,但最后,还是有很多相同的缺点产生.请在 讨论论坛与作者及其他读者分享您对本文的看法. 程序中除了最无关紧要的部分外都要对某些数据类型进行操作.静态类型系统提供了一种方法,

诊断Java代码

诊断Java代码: Broken Dispatch错误模式 诊断Java代码: Double Descent错误模式 诊断Java代码: Impostor Type错误模式 诊断Java代码: Java编程中的断言和时态逻辑 诊断Java代码: Liar View错误模式 诊断Java代码: Repl提供交互式评价 诊断Java代码: 单元测试与自动化代码分析协同工作 诊断Java代码: 将时态逻辑用于错误模式 诊断Java代码: 进行记录器测试以正确调用方法 诊断Java代码: 空标志错误模式

诊断Java代码: 进行记录器测试以正确调用方法

用 JUnit进行单元测试是一个功能强大的方法,它可以确保您的代码基础的完整性,但是一些不变量比其他(方法调用序列是其中一种)更难测试.在诊断Java 代码这一部分,Eric Allen描述了怎样在您的单元测试中使用记录器(一种特殊的侦听器),来确保一个方法调用序列按恰当的顺序发生.请点击文章顶部和底部的 讨论,与作者和其他读者在论坛上分享您关于本文的看法. 随着时间的推移,当系统开发人员,维护人员甚至是系统详细说明改变时,JUnit 框架提供一个很好的方法来改善系统的坚固性.通过测试,您可以检

诊断Java代码: 设计可扩展应用程序,第3部分

对应于我们上一篇" 诊断 Java 代码"中所讨论的透明盒可扩展性,黑盒可扩展性是指,在源代码既不能查看也不能修改时,可以扩展软件系统的方法.通常通过系统配置或使用特定于应用程序的脚本语言来进行这样的扩展.在本专题中,Eric Allen 讨论了何时设计黑盒可 扩展性的系统是有意义的,并提供了如何有效地实现这一设计的一些想法.阅读了本文后,您将知道何时使用黑盒并掌握如何实现它的一些技巧. 我已在以前的文章中谈到了代码重用设计策略的重要性(主要是因为各种信息处理任务的差异和相应费用的增加

诊断Java代码: 设计可扩展的应用程序,第2部分

玻璃箱可扩展性是指这样一种方式:软件系统可在源代码可以查看而不可以修改时被扩展 ― 它是黑箱设计(在这里构建扩展时,不查看原始代码)和开放箱设计(扩展代码直接写入到基础代码)的折衷.因为新的扩展直接建立在原始代码基础上,但不改动原始代码,所以,玻璃箱设计或许是扩展一个软件系统最有效.最安全的方法.在 诊断 Java 代码的这一部分中,Eric Allen 详述了上个月谈及的玻璃箱可扩展性主题.读完本文后,您将知道什么时候使用玻璃箱,并将获得一些如何实现它的提示. 随着信息处理任务(和与之相关的成

诊断Java代码: 将时态逻辑用于错误模式

尽管传统的断言可以增加对 Java 代码所作的检查次数,但仅用它们,还是有许多检查无法完成.处理这种情况的方法之一就是使用 时态逻辑.请回忆上个月的文章" Assertions and temporal logic in Java programming",时态逻辑有助于提供比程序中的方法更有力的断言,从而有助于增强用其它方式难以正式表达的不变量. 我们不必费力搜寻去发现有助于防止我们程序出错的许多有用的不变量.实际上,可以通过使用此类时态逻辑断言来加大我们消除一些最常见错误模式的力度

诊断Java代码: 臆想实现错误模式,第2部分

臆想实现重温 回想一下 上次接口的 臆想实现是一个合法的实现,但不满足接口规范的某些未经检查的方面.我们考虑一下下面的堆栈接口,以及许多未被其单独的类型签名捕获的不变量: 清单 1. 一个堆栈接 public interface Stack { public Object pop(); public void push(Object top); public boolean isEmpty(); } 例如,请考虑我们希望任意堆栈实现都遵守的下列规则: 如果一个对象 o 被压进堆栈 s ,且在堆栈

诊断Java代码: Java编程中的断言和时态逻辑

虽然传统断言可以增加对 Java 代码执行的检查次数,但有许多检查不能用它们来执行.弥补这一缺陷的方法是使用"时态逻辑",它是一种用于描述程序状态如何随时间而更改的形式体系.在本文中,Eric Allen 将讨论断言,介绍时态逻辑并描述用于处理程序中时态逻辑断言的工具 (下一篇文章将检查以前的错误模式和时态逻辑的应用程序). 我们大家同意对 Java 代码检查得越多就越好,我们检查了断言在测试新的和改进的编程中的用法.虽然传统断言可以增加执行的检查次数,但有许多检查不能用它们来执行.

诊断 Java 代码:设计轻松的代码维护

设计 本月,Eric Allen 解释了在使代码更易于维护的同时,避免和控制无理由的变化怎么会是保持代码健壮性的关键.他集中讨论了诸如函数样式代码编写之类的概念,以及标记字段.方法和类的方法来处理并防止可变性.Eric 还解释了本任务中单元测试和重构的角色,并提供了协助实现重构的两个工具.在相关论坛中与作者和其他读者分享您对本文的看法.(您也可以单击本文顶部或底部的"讨论",访问该论坛.)有效调试源自良好的编程.设计易于维护的程序是程序员面临的最困难挑战之一,其部分原因在于程序通常并不