练习 1.4:解决线程瓶颈

在开始之前,必须完成练习 1.3:识别线程瓶颈

要找到代码中的死锁,除了使用“线程视图”之外,还可使用“UML2 序列图”(“对象交互与线程交互”视图)和“概要分析监视器”中的“调用堆栈”概要分析资源。

要解决此死锁,请首先找出问题涉及的方法调用和对象:

  1. 在“线程视图”中,查找进入“等待锁定”状态的第一个 philo* 线程。 将光标停留在“等待锁定”段。工具提示将识别线程正在等待的锁定:Fork.<id number>
  2. 右键单击要运行的“概要分析”资源,并选择打开方式 > UML2 对象交互。 打开“UML2 序列图”视图,显示“对象交互”。
  3. 在序列图中,滚动视图查找 Fork.<id number>,并双击以选择它。
  4. 向下滚动查找从一个 philo* 线程指向 Fork.<id number> 的水平箭头。 此箭头显示对象间的交互,并且这两个对象之间的第一个交互将指出 philo* 线程已获取 Fork.<id number>。 您将发现带有 getName 标签的箭头。
  5. 双击 getName。在“线程视图”中,垂直的“当前时间”指示符移至显示,以查看调用 getName 时在整个程序中发生的事情。 您将看到请求成功,因为发出请求的 philo* 线程没有进入“等待锁定”状态。
  6. 在序列图中,再次双击 Fork.<id number>,并向下滚动以查找在不同的 philo* 线程中发出的 getName 请求。
  7. 双击此 getName 实例。“当前时间”指示器将显示发出请求的 philo* 线程没有获取 Fork.<id number>,并且进入“等待锁定”状态。

在此实例中,问题在于另一线程占用了 Fork 的请求。检查其它以“等待锁定”状态结束的线程, 以验证在其它实例中也是这样。

现在我们来尝试查找导致此问题的方法:

  1. 右键单击要运行的“概要分析”资源,并选择打开方式 > UML2 线程交互。 打开“UML2 序列图”视图,显示“线程交互”。
  2. 在概要分析监视器中,依次展开“概要分析”资源、“线程分析”条目和“调用堆栈”条目。
  3. 在显示线程名称的“线程视图”中,双击进入“正在等待锁定”状态的第一个 philo* 线程。注意“线程交互”视图将更改为只显示此线程的信息。
  4. 向下滚动到线程信息的最后,并双击此线程运行的最后一个方法:run 方法。 概要分析监视器中的“调用堆栈”此时将显示堆栈中的所有调用。
  5. 在“调用堆栈”中, 注意到保存锁定的线程已调用了 Philosopher.java 中的 Sleep 方法;或也在等待锁定并因此没采取任何操作。
  6. 检查以等待锁定结束运行的其它线程;Philosopher.java 中的 Sleep 方法经常在“调用堆栈”中,可能是问题所在。

我们现在怀疑 Sleep 方法。我们来查看一下代码:

  1. 在“调用堆栈”中,右键单击 Sleep(int) void [Philosopher.java] 的实例并选择打开资源。 在编辑器中打开资源,处于 Sleep 类的位置。
  2. 检验代码。 注意到 Sleep 方法是从 run 方法内调用的。 首先调用的是 trace,它打印出消息“got left...”,然后调用 Sleep。 通过注释掉 Sleep 的调用,我们可能能够阻止死锁。
  3. 注释掉 Sleep
  4. 选择文件 > 保存
现在再次概要分析您的程序。

这次运行没有死锁,并且将以下内容写入控制台:

HeadWaiter 报告所有 philosopher 已正常完成

正如您所见,“线程视图”和其它视图向您显示您的程序在运行时线程发生的问题。 基于您对程序的认识,是否执行分析和解决死锁完全由您来决定。

复习摘要中的资料,完成教程。

使用条款 | 反馈
(C) Copyright IBM Corporation 2000, 2005. All Rights Reserved.