- 浏览: 180038 次
- 性别:
- 来自: 厦门
文章分类
- 全部博客 (186)
- Ant (11)
- Axis2 (15)
- Car (9)
- Eclipse (1)
- Java (19)
- Java-EM (4)
- Javascript (11)
- Jsp (1)
- Hibernate (9)
- Mysql (1)
- Ms-Dos (5)
- Music (0)
- Oracle (3)
- Postgresql (0)
- Photoshop (1)
- Spring (17)
- Struts (8)
- Selenium (5)
- Ubuntu (13)
- News (17)
- Others (7)
- SSH (11)
- 算法 (5)
- FreeMarker (4)
- Tomcat (2)
- Linux (5)
最新评论
Java 理论与实践: 嗨,我的线程到哪里去了?了解如何避免服务器应用程序中的线程泄漏 |
级别: 初级 Brian Goetz (brian@quiotix.com), 首席顾问, Quiotix Corp 2002 年 12 月 09 日 如果您不小心,线程可能会从服务器应用程序中消失而没有(堆栈)跟踪。在本文中,线程问题专家 Brian Goetz 提供了用于防止和检测“擅离职守”的线程的技术。 当单线程应用程序中的主线程抛出一个未捕获的异常时,因为控制台中会打印堆栈跟踪(也因为程序停止),所以您很可能注意到。但在多线程应用程序中,尤其是 在作为服务器运行并且不与控制台相连的应用程序中,线程死亡可能成为不太引人注目的事件,这会导致局部系统失败,从而产生混乱的应用程序行为。 在 Java theory and practice十月份的专栏文章 中, 我们研究了线程池,并研究了编写得不正确的线程池会如何“泄漏”线程,直到最终丢失所有线程。大多数线程池实现通过捕获抛出的异常或重新启动死亡的线程来 防止这一点,但线程泄漏的问题并不仅限于线程池 ― 使用线程来为工作队列提供服务的服务器应用程序也可能具有这种问题。当服务器应用程序丢失了一个工作线程(worker thread)时,在较长时间内应用程序仍可能显得一切正常,这使得该问题的真实原因难以确定。 许多应用程序用线程来提供后台服务 ― 处理来自事件队列的任务、从套接字读取命令或执行 UI 线程以外的长期任务。当由于抛出未捕获的 有时,譬如当线程执行由用户启动的长期任务(如拼写检查)时,用户会注意到任务没有进展,他们可能会异常终止操作或程序。但其它时间,后台线程执行“清理维护”任务 ,它们可能消失很长时间而不被察觉。 考虑这样一个假设的中间件服务器应用程序,它聚合来自各种输入源的消息,然后将它们提交到外部服务器应用程序,从外部应用程序接收响应并将响应路由回适当 的输入源。对于每个输入源,都有一个以其自己的方式接受其输入消息的插件(通过扫描文件目录、等待套接字连接、轮询数据库表等)。插件可以由第三方编写, 即使它们是在服务器 JVM 上运行的。这个应用程序拥有(至少)两个内部工作队列 ― 从插件处接收的正在等待被发送到服务器的消息(“出站消息”队列),以及从服务器接收的正在等待被传递到适当插件的响应(“入站响应”队列)。通过调用插 件对象上的服务例程 从插件接收消息后,就被排列到出站消息队列中。由一个或多个从队列读取消息的线程处理出站消息队列中的消息、记录其来源并将它提交给远程服务器应用程序 (假定通过 Web 服务接口)。远程应用程序最终通过 Web 服务接口返回响应,然后我们的服务器将接收的响应排列到入站响应队列中。一个或多个响应线程从入站响应队列读取消息并将其路由到适当的插件,从而完成往返 “旅程”。 在这个应用程序中,有两个消息队列,分别用于出站请求和入站响应,不同的插件内可能也有另外的队列。我们还有几种服务线 程,一个从出站消息队列读取请求并将其提交给外部服务器,一个从入站响应队列读取响应并将其路由到插件,在用于向套接字或其它外部请求源提供服务的插件中 可能也有一些线程。
如果这些线程中的一个(如响应分派线程)消失了,将会发生什么?因为插件仍能够提交新消息,所以它们可能不会立即注意到某些方面出错了。消息仍将通过各种 输入源到达,并通过我们的应用程序提交到外部服务。因为插件并不期待立即获得其响应,因此它仍没有意识到出了问题。最后,接收的响应将排满队列。如果它们 存储在内存中,那么最终将耗尽内存。即使不耗尽内存,也会有人在某个时刻发现响应得不到传递 ― 但这可能需要一些时间,因为系统的其它方面仍能正常发挥作用。 当主要的任务处理方面由线程池而不是单个线程来处理时,对于偶然的线程泄漏的后果有一定程度的保护,因为一个执行得很好的 八线程的线程池,用七个线程完成其工作的效率可能仍可以接受。起初,可能没有任何显著的差异。但是,系统性能最终将下降,虽然这种下降的方式不易被察觉。 服务器应用程序中的线程泄漏问题在于不是总是容易从外部检测它。因为大多数线程只处理服务器的部分工作负载,或可能仅处理特定类型的后台任务,所以当程序 实际上遭遇严重故障时,在用户看来它仍在正常工作。这一点,再加上引起线程泄漏的因素并不总是留下明显痕迹,就会引起令人惊讶甚或使人迷惑的应用程序行 为。
当线程抛出未捕获的异常或错误时它们可能消失;而当线程等待的 I/O 操作永远不会完成,或没人为它们等待的监视器调用 当线程抛出未捕获的异常或错误时它们可能消失;而当线程等待的 I/O 操作永远不会完成,或没人为它们等待的监视器调用 notify() 时,它们只是停止工作。意外线程死亡的最常见根源是 处理任务的线程(类似于示例应用程序中的请求和响应处理程序),基本上花费其整个生命周期穿过某个类似于 清单 1 中的代码是典型的从工作队列处理 清单 1. 不防备 RuntimeException 的工作线程
我们不必添加许多代码来使这个工作线程能够更健壮地处理插件代码中的故障。只要通过捕获 清单 2. 防备 RuntimeException 的工作线程
除了将外来代码视作较可能抛出 清单 3. 使用 uncaughtException 来检测线程死亡
如果线程组中的一个线程因抛出一个未捕获的异常而死亡,则调用该线程组的
当线程从应用程序中消失时会引起混乱,并且在很多情况下,线程消失时没有(堆栈)跟踪。象对付许多风险一样,防止线程泄漏的最佳方法是预防和检测相结合;注意有可能抛出 |
发表评论
-
JAVA中建立多线程的典型例子
2008-10-24 21:23 749JAVA中建立多线程,无非两种方式,一是继承自thread类 ... -
解决 Java 程序的乱码问题
2008-10-28 19:19 672早上来上班,同事问在本机好好的 Java 程序,放到服务器上运 ... -
编写对GC友好,又不泄漏的代码
2008-10-31 14:07 668编写对GC友好,又不泄 ... -
java实现给图片添加水印和文字
2008-10-31 18:49 782import java.io.*; import com.su ... -
当我调用 ProductService继承BaseService的方法时,提示我BaseDao属性null
2008-10-31 18:59 911转自:http://hi.baidu.com/annleecn ... -
Java实现分类文件拷贝1
2008-10-31 19:12 961在Java中对文件进行分类 如果你是个Java程序员,也许你 ... -
Java实现分类文件拷贝2
2008-10-31 19:13 693对于对老文件的修改,我 ... -
Java实现分类文件拷贝3
2008-10-31 19:14 699java.io.BufferedWriter; import ... -
Java实现分类文件拷贝4
2008-10-31 19:15 679tputStream fos=new FileOutputSt ... -
j2ee清除缓存的三句话
2008-10-31 19:19 515在ajax中经常会遇到一个问题,就是有重复请求的时候,ajax ... -
java中回调的实现
2008-10-31 19:22 647//定义一个接口 interface Incrementabl ... -
java实现快速排序算法
2008-10-31 19:23 654void paixu(int a[],int low,int ... -
java的窗口关闭问题
2008-10-31 19:24 968java的窗口关闭可以实现windowLinstener接口来 ... -
关于 DAO 接口设计的思考
2008-11-07 11:03 673需求: 现在要设计 3 个 DAO 分对 Wind, Hors ... -
如何比较两个有可能为null的实例
2008-11-09 16:37 816如何比较两个有可能为null的实例 以前在写程序 ... -
详细解析Java中抽象类和接口的区别
2008-11-26 20:39 680详细解析Java中抽象类 ... -
Java获取系统所有进程(转)
2009-02-06 12:36 927代码比较简单,就不那么讲究了。一个 main 写了吧。 im ... -
MD5加密 Java代码
2008-12-15 19:29 807因为代码太长,无法贴上来,所以传到了网盘上,可以下载 点击下 ...
相关推荐
本文介绍了当线程从应用程序中消失时会引起混乱,并且在很多情况下,线程消失时没有(堆栈)跟踪。像对付许多风险一样,防止线程泄漏的最佳方法是预防和检测相结合;注意有可能抛出RuntimeException的地方(如调用...
Java内存模型(Java Memory Model, JMM)是Java平台中用于规范线程间通信和内存可见性的重要概念,它的目标是确保多线程环境下的正确同步。然而,原始的JMM存在一些严重的缺陷,导致了开发者在理解和实现线程安全时...
Java中的线程安全性是并发编程中的关键概念,它关乎到多线程环境下程序的稳定性和正确性。线程安全的类意味着在多个线程并行访问时,它们的行为仍然是正确和一致的,无需额外的同步措施。然而,线程安全并不简单地...
Java 作为一种垃圾收集语言,确实减轻了程序员对内存管理的负担,但这并不意味着对象所有权的问题完全消失。在Java中,虽然不再需要手动调用...通过理解和运用适当的编程实践,可以确保Java应用程序的性能和稳定性。
Java语言规范中指出:为了获得佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。 这样当多个线程同时与某个对象交互时,必须要注意到要让线程...
并说明了在Java理论与实践中,不变性的一些长处、何时使用不变类和构造不变类的一些准则。使用不变对象比使用可变对象要容易得多。它们只能处于一种状态,所以始终是一致的,它们本来就是线程安全的,可以被自由地...
Java理论与实践再研究的知识点覆盖了Java编程语言的多个重要方面。从基本的入门指南,到深入探讨面向对象的高级特性,再到异常处理、泛型编程和图形用户界面设计,本文件为读者提供了一个系统的学习路径,以深化对...
《Java理论与实践:构建一个更好的HashMap》这篇文章深入剖析了Doug Lea的`util.concurrent`包中的`ConcurrentHashMap`实现,旨在展示如何在保证线程安全的同时提高并发性能。`ConcurrentHashMap`相较于传统的`...
Java内存模型(Java Memory Model, JMM)是Java平台中用于确保多线程程序正确同步的关键部分。在早期版本的Java中,JMM存在一些漏洞,导致开发者在编写并发程序时面临挑战,因为它们不能保证在所有平台上都能正确...
本文介绍了要使用多处理器系统的功能,通常需要使用多线程构造应用程序。但是正如任何编写并发应用程序的人可以告诉你的那样,要获得好的硬件利用率,只是简单地在多个线程中分割工作是不够的,还必须确保线程确实大...
另一个常见的线程模型是为某一类型的任务分配一个后台线程与任务队列。AWT和Swing就使用这个模型,在这个模型中有一个GUI事件线程,导致用户界面发生变化的所有工作都必须在该线程中执行。然而,由于只有一个AWT线程...
11. **实战案例**:书中可能提供实际的并发编程例子,帮助读者将理论知识应用到实践中。 通过学习《深入学习:Java多线程编程》,开发者可以提升在复杂并发环境下的编程能力,为开发高并发、高可用的应用打下坚实的...
本文介绍了在Java类库中出现的第一个关联的集合类是Hashtable,它是JDK 1.0的一部分。Hashtable提供了一种易于使用的、线程安全的、关联的map功能,这当然也是方便的。然而,线程安全性是凭代价换来的―― Hashtable...
本实验不仅实现了预期的功能,还提供了实践机会,帮助理解和掌握Java多线程编程的关键技术和注意事项。通过实验,我们可以更加深刻地理解多线程编程的优势与挑战,为后续学习高级并发编程打下坚实的基础。
并简单概述了老对象和年轻对象、分代收集、小的收集、代间引用、跟踪代间引用、卡片标记、JDK 1.4.1 默认收集器、并行收集器和并发收集器、微调垃圾收集器等理论或技术。得出:随着JVM的发展,默认垃圾收集器变得...
Java 并发学习笔记: 进程和线程, 并发理论, 并发关键字, Lock 体系, 原子操作类, 发容器 & 并发工具, 线程池, 并发实践 Java是一种面向对象的编程语言,由Sun Microsystems于1995年推出。它是一种跨平台的...