- 浏览: 650095 次
- 性别:
- 来自: 成都
最新评论
-
zhima:
楼主还在升级开发吗?还是地址变动了
开源WebSocket服务器项目宝贝鱼CshBBrain V4.0.1 和 V2.0.2发布 -
linxingliang:
你好,我有一个iteye账号也被锁定了,你是通过什么方式联系管 ...
今天由于iteye账号被锁定所有博文不能访问,已解锁 -
独自空楼茉莉为谁而开:
解决啦,仿照你的广播发送写的代码 嘿嘿
开源WebSocket服务器项目 宝贝鱼(CshBBrain)版本发布 -
独自空楼茉莉为谁而开:
小弟有一个问题 我在本地开启两个页面访问后台,后台稍微修改下代 ...
开源WebSocket服务器项目 宝贝鱼(CshBBrain)版本发布 -
spqin:
...
百度地图API详解之地图标注
转自http://www.ibm.com/developerworks/cn/java/j-jtp0924/
Java 理论与实践: 嗨,我的线程到哪里去了?
了解如何避免服务器应用程序中的线程泄漏
Brian Goetz (brian@quiotix.com), 首席顾问, Quiotix Corp
当单线程应用程序中的主线程抛出一个未捕获的异常时,因为控制台中会打印堆栈跟踪(也因为程序停止),所以您很可能注意到。但在多线程应用程序中,尤其是在作为服务器运行并且不与控制台相连的应用程序中,线程死亡可能成为不太引人注目的事件,这会导致局部系统失败,从而产生混乱的应用程序行为。
在 Java theory and practice十月份的专栏文章 中,我们研究了线程池,并研究了编写得不正确的线程池会如何“泄漏”线程,直到最终丢失所有线程。大多数线程池实现通过捕获抛出的异常或重新启动死亡的线程来防止这一点,但线程泄漏的问题并不仅限于线程池 ― 使用线程来为工作队列提供服务的服务器应用程序也可能具有这种问题。当服务器应用程序丢失了一个工作线程(worker thread)时,在较长时间内应用程序仍可能显得一切正常,这使得该问题的真实原因难以确定。
许多应用程序用线程来提供后台服务 ― 处理来自事件队列的任务、从套接字读取命令或执行 UI 线程以外的长期任务。当由于抛出未捕获的 RuntimeException 或 Error ,或者只是停下来,等待阻塞的 I/O 操作(原本未预计到阻塞),从而引起这些线程之一死亡时,会发生什么呢?
有时,譬如当线程执行由用户启动的长期任务(如拼写检查)时,用户会注意到任务没有进展,他们可能会异常终止操作或程序。但其它时间,后台线程执行“清理维护”任务 ,它们可能消失很长时间而不被察觉。
示例服务器应用程序
考虑这样一个假设的中间件服务器应用程序,它聚合来自各种输入源的消息,然后将它们提交到外部服务器应用程序,从外部应用程序接收响应并将响应路由回适当的输入源。对于每个输入源,都有一个以其自己的方式接受其输入消息的插件(通过扫描文件目录、等待套接字连接、轮询数据库表等)。插件可以由第三方编写,即使它们是在服务器 JVM 上运行的。这个应用程序拥有(至少)两个内部工作队列 ― 从插件处接收的正在等待被发送到服务器的消息(“出站消息”队列),以及从服务器接收的正在等待被传递到适当插件的响应(“入站响应”队列)。通过调用插件对象上的服务例程 incomingResponse() ,消息被路由到最初发出请求的插件。
从插件接收消息后,就被排列到出站消息队列中。由一个或多个从队列读取消息的线程处理出站消息队列中的消息、记录其来源并将它提交给远程服务器应用程序(假定通过 Web 服务接口)。远程应用程序最终通过 Web 服务接口返回响应,然后我们的服务器将接收的响应排列到入站响应队列中。一个或多个响应线程从入站响应队列读取消息并将其路由到适当的插件,从而完成往返“旅程”。
在这个应用程序中,有两个消息队列,分别用于出站请求和入站响应,不同的插件内可能也有另外的队列。我们还有几种服务线程,一个从出站消息队列读取请求并将其提交给外部服务器,一个从入站响应队列读取响应并将其路由到插件,在用于向套接字或其它外部请求源提供服务的插件中可能也有一些线程。
线程失败时并不总是显而易见的
如果这些线程中的一个(如响应分派线程)消失了,将会发生什么?因为插件仍能够提交新消息,所以它们可能不会立即注意到某些方面出错了。消息仍将通过各种输入源到达,并通过我们的应用程序提交到外部服务。因为插件并不期待立即获得其响应,因此它仍没有意识到出了问题。最后,接收的响应将排满队列。如果它们存储在内存中,那么最终将耗尽内存。即使不耗尽内存,也会有人在某个时刻发现响应得不到传递 ― 但这可能需要一些时间,因为系统的其它方面仍能正常发挥作用。
当主要的任务处理方面由线程池而不是单个线程来处理时,对于偶然的线程泄漏的后果有一定程度的保护,因为一个执行得很好的八线程的线程池,用七个线程完成其工作的效率可能仍可以接受。起初,可能没有任何显著的差异。但是,系统性能最终将下降,虽然这种下降的方式不易被察觉。
服务器应用程序中的线程泄漏问题在于不是总是容易从外部检测它。因为大多数线程只处理服务器的部分工作负载,或可能仅处理特定类型的后台任务,所以当程序实际上遭遇严重故障时,在用户看来它仍在正常工作。这一点,再加上引起线程泄漏的因素并不总是留下明显痕迹,就会引起令人惊讶甚或使人迷惑的应用程序行为。
RuntimeException 是导致线程死亡的首要原因
当线程抛出未捕获的异常或错误时它们可能消失;而当线程等待的 I/O 操作永远不会完成,或没人为它们等待的监视器调用 notify() 时,它们只是停止工作。意外线程死亡的最常见根源是 RuntimeException (如 NullPointerException 、 ArrayIndexOutOfBoundsException 等)。 在我们的示例应用程序中,在通过调用插件对象上的 incomingResponse() 将响应传递回插件时,可能抛出 RuntimeException 。插件代码可能是由第三方编写的,或者可能是在编写完应用程序之后编写的,因此应用程序编写者不可能审核其正确性。如果一些插件抛出 RuntimeException 时某些响应服务线程会终止,这意味着一个出错的插件会使整个系统崩溃。遗憾的是,这种脆弱性很常见。
当线程抛出未捕获的异常或错误时它们可能消失;而当线程等待的 I/O 操作永远不会完成,或没人为它们等待的监视器调用 notify() 时,它们只是停止工作。意外线程死亡的最常见根源是 RuntimeException 的结果很明显,并且对发生异常的位置有明确的堆栈跟踪,这提供了问题通知以及解决问题的有用信息。但是,在多线程应用程序中,由于未查出的异常,线程会无声无息地死亡 — 使得用户和开发人员对于发生的问题和为什么发生这些问题毫无头绪。
处理任务的线程(类似于示例应用程序中的请求和响应处理程序),基本上花费其整个生命周期穿过某个类似于 Runnable 的抽象障碍物来调用服务方法。因为我们不知道在这个抽象障碍物的另一边是什么,所以,对于服务方法,我们应该怀疑,它是不是真好到可以假设它从不抛出未查出异常的程度。如果服务例程抛出 RuntimeException ,则调用线程应该捕获这个异常,并将它记录到日志,然后转到队列中的下一项或关闭线程然后再重新启动它。(后一个选项源自这样的假定:任何抛出 RuntimeException 或 Error 的代码也可能已经破坏了线程的状态。)
清单 1 中的代码是典型的从工作队列处理 Runnable 任务的线程,类似于我们的示例中的入站响应线程。它并不防备抛出任何未查出异常的插件。
清单 1. 不防备 RuntimeException 的工作线程
private class TrustingPoolWorker extends Thread {
public void run() {
IncomingResponse ir;
while (true) {
ir = (IncomingResponse) queue.getNext();
PlugIn plugIn = findPlugIn(ir.getResponseId());
if (plugIn != null)
plugIn.handleMessage(ir.getResponse());
else
log("Unknown plug-in for response " + ir.getResponseId());
}
}
}
我们不必添加许多代码来使这个工作线程能够更健壮地处理插件代码中的故障。只要通过捕获 RuntimeException ,然后进行纠正操作,就可以确保我们自己有能力防止一个编写得较差的插件破坏整个服务器。适当的纠正操作应该将错误记录到日志,然后,简单地转到下一条消息,终止当前线程并重新启动它(这是类似于 TimerTask 的类的做法),或者卸载引起问题的插件,如清单 2 中所示:
清单 2. 防备 RuntimeException 的工作线程
private class SaferPoolWorker extends Thread {
public void run() {
IncomingResponse ir;
while (true) {
ir = (IncomingResponse) queue.getNext();
PlugIn plugIn = findPlugIn(ir.getResponseId());
if (plugIn != null) {
try {
plugIn.handleMessage(ir.getResponse());
}
catch (RuntimeException e) {
// Take some sort of action;
// - log the exception and move on
// - log the exception and restart the worker thread
// - log the exception and unload the offending plug-in
}
}
else
log("Unknown plug-in for response " + ir.getResponseId());
}
}
}
使用由 ThreadGroup 提供的未捕获的异常处理程序
除了将外来代码视作较可能抛出 RuntimeException 的方法之外,使用 ThreadGroup 类的 uncaughtException 函数也是明智的。 ThreadGroup 用处不很大,但是目前(直到 JDK 1.5 中的 Thread 添加了未捕获的异常处理为止), uncaughtException 特性暂时使它不可或缺。清单 3 展示了一个示例,使用 ThreadGroup 来检测由于未捕获的异常引起的线程死亡。
清单 3. 使用 uncaughtException 来检测线程死亡
public class ThreadGroupExample {
public static class MyThreadGroup extends ThreadGroup {
public MyThreadGroup(String s) {
super(s);
}
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println("Thread " + thread.getName()
+ " died, exception was: ");
throwable.printStackTrace();
}
}
public static ThreadGroup workerThreads =
new MyThreadGroup("Worker Threads");
public static class WorkerThread extends Thread {
public WorkerThread(String s) {
super(workerThreads, s);
}
public void run() {
throw new RuntimeException();
}
}
public static void main(String[] args) {
Thread t = new WorkerThread("Worker Thread");
t.start();
}
}
如果线程组中的一个线程因抛出一个未捕获的异常而死亡,则调用该线程组的 uncaughtException() 方法,该方法可以向日志写入一条记录、重新启动线程,然后重新启动系统,或采取它认为必要的任何纠正或诊断操作。至少,如果在线程死亡时所有线程都写一条日志消息,您将有一个何时、何处出错的记录,而不是只能奇怪您的请求处理线程到哪里去了。
结束语
当线程从应用程序中消失时会引起混乱,并且在很多情况下,线程消失时没有(堆栈)跟踪。象对付许多风险一样,防止线程泄漏的最佳方法是预防和检测相结合;注意有可能抛出 RuntimeException 的地方(如调用外来代码时),并使用 ThreadGroup 提供的 uncaughtException 处理程序来在线程异常终止时进行检测。
Java 理论与实践: 嗨,我的线程到哪里去了?
了解如何避免服务器应用程序中的线程泄漏
Brian Goetz (brian@quiotix.com), 首席顾问, Quiotix Corp
当单线程应用程序中的主线程抛出一个未捕获的异常时,因为控制台中会打印堆栈跟踪(也因为程序停止),所以您很可能注意到。但在多线程应用程序中,尤其是在作为服务器运行并且不与控制台相连的应用程序中,线程死亡可能成为不太引人注目的事件,这会导致局部系统失败,从而产生混乱的应用程序行为。
在 Java theory and practice十月份的专栏文章 中,我们研究了线程池,并研究了编写得不正确的线程池会如何“泄漏”线程,直到最终丢失所有线程。大多数线程池实现通过捕获抛出的异常或重新启动死亡的线程来防止这一点,但线程泄漏的问题并不仅限于线程池 ― 使用线程来为工作队列提供服务的服务器应用程序也可能具有这种问题。当服务器应用程序丢失了一个工作线程(worker thread)时,在较长时间内应用程序仍可能显得一切正常,这使得该问题的真实原因难以确定。
许多应用程序用线程来提供后台服务 ― 处理来自事件队列的任务、从套接字读取命令或执行 UI 线程以外的长期任务。当由于抛出未捕获的 RuntimeException 或 Error ,或者只是停下来,等待阻塞的 I/O 操作(原本未预计到阻塞),从而引起这些线程之一死亡时,会发生什么呢?
有时,譬如当线程执行由用户启动的长期任务(如拼写检查)时,用户会注意到任务没有进展,他们可能会异常终止操作或程序。但其它时间,后台线程执行“清理维护”任务 ,它们可能消失很长时间而不被察觉。
示例服务器应用程序
考虑这样一个假设的中间件服务器应用程序,它聚合来自各种输入源的消息,然后将它们提交到外部服务器应用程序,从外部应用程序接收响应并将响应路由回适当的输入源。对于每个输入源,都有一个以其自己的方式接受其输入消息的插件(通过扫描文件目录、等待套接字连接、轮询数据库表等)。插件可以由第三方编写,即使它们是在服务器 JVM 上运行的。这个应用程序拥有(至少)两个内部工作队列 ― 从插件处接收的正在等待被发送到服务器的消息(“出站消息”队列),以及从服务器接收的正在等待被传递到适当插件的响应(“入站响应”队列)。通过调用插件对象上的服务例程 incomingResponse() ,消息被路由到最初发出请求的插件。
从插件接收消息后,就被排列到出站消息队列中。由一个或多个从队列读取消息的线程处理出站消息队列中的消息、记录其来源并将它提交给远程服务器应用程序(假定通过 Web 服务接口)。远程应用程序最终通过 Web 服务接口返回响应,然后我们的服务器将接收的响应排列到入站响应队列中。一个或多个响应线程从入站响应队列读取消息并将其路由到适当的插件,从而完成往返“旅程”。
在这个应用程序中,有两个消息队列,分别用于出站请求和入站响应,不同的插件内可能也有另外的队列。我们还有几种服务线程,一个从出站消息队列读取请求并将其提交给外部服务器,一个从入站响应队列读取响应并将其路由到插件,在用于向套接字或其它外部请求源提供服务的插件中可能也有一些线程。
线程失败时并不总是显而易见的
如果这些线程中的一个(如响应分派线程)消失了,将会发生什么?因为插件仍能够提交新消息,所以它们可能不会立即注意到某些方面出错了。消息仍将通过各种输入源到达,并通过我们的应用程序提交到外部服务。因为插件并不期待立即获得其响应,因此它仍没有意识到出了问题。最后,接收的响应将排满队列。如果它们存储在内存中,那么最终将耗尽内存。即使不耗尽内存,也会有人在某个时刻发现响应得不到传递 ― 但这可能需要一些时间,因为系统的其它方面仍能正常发挥作用。
当主要的任务处理方面由线程池而不是单个线程来处理时,对于偶然的线程泄漏的后果有一定程度的保护,因为一个执行得很好的八线程的线程池,用七个线程完成其工作的效率可能仍可以接受。起初,可能没有任何显著的差异。但是,系统性能最终将下降,虽然这种下降的方式不易被察觉。
服务器应用程序中的线程泄漏问题在于不是总是容易从外部检测它。因为大多数线程只处理服务器的部分工作负载,或可能仅处理特定类型的后台任务,所以当程序实际上遭遇严重故障时,在用户看来它仍在正常工作。这一点,再加上引起线程泄漏的因素并不总是留下明显痕迹,就会引起令人惊讶甚或使人迷惑的应用程序行为。
RuntimeException 是导致线程死亡的首要原因
当线程抛出未捕获的异常或错误时它们可能消失;而当线程等待的 I/O 操作永远不会完成,或没人为它们等待的监视器调用 notify() 时,它们只是停止工作。意外线程死亡的最常见根源是 RuntimeException (如 NullPointerException 、 ArrayIndexOutOfBoundsException 等)。 在我们的示例应用程序中,在通过调用插件对象上的 incomingResponse() 将响应传递回插件时,可能抛出 RuntimeException 。插件代码可能是由第三方编写的,或者可能是在编写完应用程序之后编写的,因此应用程序编写者不可能审核其正确性。如果一些插件抛出 RuntimeException 时某些响应服务线程会终止,这意味着一个出错的插件会使整个系统崩溃。遗憾的是,这种脆弱性很常见。
当线程抛出未捕获的异常或错误时它们可能消失;而当线程等待的 I/O 操作永远不会完成,或没人为它们等待的监视器调用 notify() 时,它们只是停止工作。意外线程死亡的最常见根源是 RuntimeException 的结果很明显,并且对发生异常的位置有明确的堆栈跟踪,这提供了问题通知以及解决问题的有用信息。但是,在多线程应用程序中,由于未查出的异常,线程会无声无息地死亡 — 使得用户和开发人员对于发生的问题和为什么发生这些问题毫无头绪。
处理任务的线程(类似于示例应用程序中的请求和响应处理程序),基本上花费其整个生命周期穿过某个类似于 Runnable 的抽象障碍物来调用服务方法。因为我们不知道在这个抽象障碍物的另一边是什么,所以,对于服务方法,我们应该怀疑,它是不是真好到可以假设它从不抛出未查出异常的程度。如果服务例程抛出 RuntimeException ,则调用线程应该捕获这个异常,并将它记录到日志,然后转到队列中的下一项或关闭线程然后再重新启动它。(后一个选项源自这样的假定:任何抛出 RuntimeException 或 Error 的代码也可能已经破坏了线程的状态。)
清单 1 中的代码是典型的从工作队列处理 Runnable 任务的线程,类似于我们的示例中的入站响应线程。它并不防备抛出任何未查出异常的插件。
清单 1. 不防备 RuntimeException 的工作线程
private class TrustingPoolWorker extends Thread {
public void run() {
IncomingResponse ir;
while (true) {
ir = (IncomingResponse) queue.getNext();
PlugIn plugIn = findPlugIn(ir.getResponseId());
if (plugIn != null)
plugIn.handleMessage(ir.getResponse());
else
log("Unknown plug-in for response " + ir.getResponseId());
}
}
}
我们不必添加许多代码来使这个工作线程能够更健壮地处理插件代码中的故障。只要通过捕获 RuntimeException ,然后进行纠正操作,就可以确保我们自己有能力防止一个编写得较差的插件破坏整个服务器。适当的纠正操作应该将错误记录到日志,然后,简单地转到下一条消息,终止当前线程并重新启动它(这是类似于 TimerTask 的类的做法),或者卸载引起问题的插件,如清单 2 中所示:
清单 2. 防备 RuntimeException 的工作线程
private class SaferPoolWorker extends Thread {
public void run() {
IncomingResponse ir;
while (true) {
ir = (IncomingResponse) queue.getNext();
PlugIn plugIn = findPlugIn(ir.getResponseId());
if (plugIn != null) {
try {
plugIn.handleMessage(ir.getResponse());
}
catch (RuntimeException e) {
// Take some sort of action;
// - log the exception and move on
// - log the exception and restart the worker thread
// - log the exception and unload the offending plug-in
}
}
else
log("Unknown plug-in for response " + ir.getResponseId());
}
}
}
使用由 ThreadGroup 提供的未捕获的异常处理程序
除了将外来代码视作较可能抛出 RuntimeException 的方法之外,使用 ThreadGroup 类的 uncaughtException 函数也是明智的。 ThreadGroup 用处不很大,但是目前(直到 JDK 1.5 中的 Thread 添加了未捕获的异常处理为止), uncaughtException 特性暂时使它不可或缺。清单 3 展示了一个示例,使用 ThreadGroup 来检测由于未捕获的异常引起的线程死亡。
清单 3. 使用 uncaughtException 来检测线程死亡
public class ThreadGroupExample {
public static class MyThreadGroup extends ThreadGroup {
public MyThreadGroup(String s) {
super(s);
}
public void uncaughtException(Thread thread, Throwable throwable) {
System.out.println("Thread " + thread.getName()
+ " died, exception was: ");
throwable.printStackTrace();
}
}
public static ThreadGroup workerThreads =
new MyThreadGroup("Worker Threads");
public static class WorkerThread extends Thread {
public WorkerThread(String s) {
super(workerThreads, s);
}
public void run() {
throw new RuntimeException();
}
}
public static void main(String[] args) {
Thread t = new WorkerThread("Worker Thread");
t.start();
}
}
如果线程组中的一个线程因抛出一个未捕获的异常而死亡,则调用该线程组的 uncaughtException() 方法,该方法可以向日志写入一条记录、重新启动线程,然后重新启动系统,或采取它认为必要的任何纠正或诊断操作。至少,如果在线程死亡时所有线程都写一条日志消息,您将有一个何时、何处出错的记录,而不是只能奇怪您的请求处理线程到哪里去了。
结束语
当线程从应用程序中消失时会引起混乱,并且在很多情况下,线程消失时没有(堆栈)跟踪。象对付许多风险一样,防止线程泄漏的最佳方法是预防和检测相结合;注意有可能抛出 RuntimeException 的地方(如调用外来代码时),并使用 ThreadGroup 提供的 uncaughtException 处理程序来在线程异常终止时进行检测。
发表评论
-
基于宝贝鱼(CshBBrain)开发聊天类应用 群发消息的问题
2013-01-12 09:30 2420最近有网友 基于宝贝鱼(CshBBrain)开发聊天类应用 遇 ... -
JS字符串函数
2012-12-15 16:03 1365JS自带函数concat将两个或多个字符的文本组合起来,返回一 ... -
开源WebSocket服务器项目 宝贝鱼(CshBBrain)版本V4.0.2 和 V2.0.3发布
2012-12-04 10:23 2089宝贝鱼 CshBBrain V4.0.2 和 CshBB ... -
开源WebSocket服务器项目 宝贝鱼(CshBBrain) 前台js 框架CshBBrainJS V1.0.0发布(有图有真相)
2012-12-04 09:48 2950为了更好的支持开发基于Websocket的应用,开源Web ... -
基于开源WebSocket服务器宝贝鱼(CshBBrain)的应用横空出世
2012-12-04 10:24 2807开源WebSocket服务器 宝贝鱼(CshBBrain) ... -
开源WebSocket服务器项目宝贝鱼CshBBrain发布新版本,修复重大广播消息缺陷
2012-11-13 10:16 2739开源WebSocket服务器项目 ... -
开源WebSocket服务器项目宝贝鱼CshBBrain V4.0.1 和 V2.0.2发布
2012-11-13 09:57 2123开源WebSocket服务器项目宝贝鱼CshBBrain ... -
JAVA AIO扫盲和入门
2012-11-05 13:33 9723开源WebSocket服务器 宝贝鱼(CshBBrain) ... -
国内首款基于AIO(异步IO)支持集群的高性能开源WebSocket服务器 宝贝鱼 CshBBrain V4.0 发布
2012-11-05 11:29 3653国内首款基于AIO的开源WebSocket服务器 宝贝鱼 ... -
开源WebSocket服务器项目CshBBrain 中文名:宝贝鱼
2012-11-03 14:38 1747有人叫我给 开源WebSocket服务器项目CshBBrain ... -
开源WebSocket服务器项目CshBBrain V2.0.1发布
2012-11-02 21:22 1633CshBBrain V2.0.1: ... -
高性能I/O设计模式Reactor和Proactor
2012-10-26 17:41 2368转自:http://hi.baidu.com/qhpgb ... -
Proactor和Reactor模式_继续并发系统设计的扫盲
2012-10-26 17:40 3874转自:http://www.cppblog.com/kevin ... -
reactor和proactor模式的比较
2012-10-26 17:38 1676转自:http://blog.163.com/zongyuan ... -
CshBBrain集群设计与开发计划
2012-10-23 14:19 18401.服务器集群交互使用的协议: 1.1 握手协议,定义 ... -
CshBBrain V2.0.0版发布,添加服务器集群功能,以满足大并发量高容量的分布式系统开发需求
2012-10-23 13:37 1563开源WebSocket服务器CshBBrain V2.0 ... -
欢迎iteye网友peacherdiy 和 文库虫 加入开源Websocket服务器CshBBrain开源项目团队
2012-10-16 15:58 12712012年10月15日开源WebSocket服务器Csh ... -
开源WebSocket服务器项目CshBBrain中NIO Buffer的使用策略
2012-10-15 10:26 2833NIO Buffer带给我们的好处是在进行I/O数据读写时 ... -
Direct Buffer vs. Heap Buffer
2012-10-15 09:33 1717下面的内容有些从网上搜罗的,有些属于自己总结的,总之就是 ... -
开源WebSocket服务器项目CshBBrain客户端超时检查机制剖析
2012-10-14 22:01 1WebSocket要求服务器与客户端之间要保持连接状 ...
相关推荐
本文介绍了当线程从应用程序中消失时会引起混乱,并且在很多情况下,线程消失时没有(堆栈)跟踪。像对付许多风险一样,防止线程泄漏的最佳方法是预防和检测相结合;注意有可能抛出RuntimeException的地方(如调用...
Java内存模型(Java Memory Model, JMM)是Java平台中用于规范线程间通信和内存可见性的重要概念,它的目标是确保多线程环境下的正确同步。然而,原始的JMM存在一些严重的缺陷,导致了开发者在理解和实现线程安全时...
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年推出。它是一种跨平台的...