练习 1.4:解决线程瓶颈

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

要找到代码中的死锁,除了使用“线程视图”之外,还可以使用“UML2 时序图”(“对象交互与线程交互”视图)和“调用堆栈视图”。

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

  1. 在“线程视图”中,查找进入“死锁”状态的第一个 philo* 线程。将光标停留在“死锁”段上。工具提示指出锁定(Fork.<id number>)和锁定线程(Locking Thread.<name>),例如:

Lock: Fork 10038
Locking Thread: philo#1

  1. 右键单击要运行的“概要分析”资源并选择打开方式 > UML2 对象交互。 打开“UML2 时序图”视图,显示“对象交互”。
  2. 在时序图中,水平滚动视图以查找 Fork.<id number>,并单击以选择它。

显示对象交互的时序图

  1. 向下滚动查找从一个 philo* 线程指向 Fork.<id number> 的水平箭头。 此箭头显示对象间的交互,并且这两个对象之间的第一个交互将指出 philo* 线程已获取 Fork.<id number>。 您将发现带有 getName 标签的箭头。

显示 getName 的时序图

  1. 单击 getName。在“线程视图”中,垂直的“当前时间”指示符移至显示,以查看调用 getName 时在整个程序中发生的事情。您将看到请求成功,因为发出请求的 philo* 线程没有进入“等待锁定”或“死锁”状态。

显示处于运行状态的 philo* 线程的线程视图

  1. 在时序图中,再次单击 Fork.<id number>,并向下滚动以查找在不同的 philo* 线程中发出的 getName 请求。
  2. 单击 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.