摘要
最有价值的调试工具是以线程为中心的。大部分 Java 错误都与线程交互有关。多线程调试让开发人员可以查看应用程序中运行的每个线程中的执行情况。
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)SUN Laura Bennett
多线程调试基础
最有价值的调试工具是以线程为中心的。大部分 Java 错误都与线程交互有关。多线程调试让开发人员可以查看应用程序中运行的每个线程中的执行情况。
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)由于执行顺序的易变性,查找多线程应用程序中的错误比非线程化情况要困难得多。如果可以按相同的可预料顺序执行指令,那么调试这些应用程序就可以变得非常简单。当然,这将违背多线程化的目标。结果,许多 IDE 调试器在这种情况下都不能起什么作用,因为单步调试代码会减缓调试过程,并禁止重新创建错误事件。
多线程错误的类型
这里有几种常见的多线程编码问题需要密切关注:
访问违规。当两个或更多线程试图访问同一个内存位置时,会发生这种问题。
死锁。譬如说 Thread1 锁定了 ResourceA,而 Thread2 锁定了 ResourceB。然后Thread1试图锁定 ResourceB,并等待 ResourceB 变成可用的。同时,Thread2试图锁定 ResourceA 并等待 ResourceA 变成可用的。 结果:死锁。防止死锁的一种方法是不要让进程在设置了锁定时睡眠。还可以使用 synchronization() 来确保关键部分的代码一次只能由一个线程访问。
数据争用错误。数据争用条件会锁定应用程序,这种情况会时常发生,譬如双击鼠标左键。在数据争用的情况下数据通常会遭到破坏。要防止这种错误,应使变量不能被多个线程访问。现在已经有工具可以分析这种问题并标志可能发生数据争用错误的变量。
同步错误。进行无用信息收集时可能会发生这种问题。Java 会自动处理无用信息收集。此时,所有线程都会从运行状态变成暂挂。
使用 synchronized() 方法
不同版本的 JVM 实现线程优先级的方法也可能不同,这会影响线程同步。我们建议您在多个操作系统上测试线程化代码,以验证它是否真正是跨平台的。
synchronized() 方法创建了一个模拟锁定的代码块。这个用同步方法描绘的代码只允许每次只有一个进程运行它。但不要使用太多的 synchronized 调用,因为它们会直接影响代码性能。实际上,同步停止了多线程化。
以下显示了使用同步化方法的代码示例。通过重新设置实例变量中表的最大的列大小,这段代码将一个元素添加到表中。可以看到多个线程更新同一个变量值可能会造成破坏。使用同步化方法有助于缓和这个问题。
(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/bianchengyuyan/)
/** Synchronized method to add an element to a table **/
public void addElement( Patient newPatient )
{
synchronized ( lock )
{
Log query = incomingPatient.getLog();
results = query.getAllSearchResults();
for ( int k = 0; k results.length; k++)
{
.... // add to table
setMaxColSize(MyTable);
tableContents.add(MyTable);
}
}
}
避免多线程错误
有一些方法可以避免可怕的线程错误:
如果依靠线程优先级来使线程保持同步,那么测试 JVM 的各种类就显得非常重要。小心可能发生两个线程同时赋值给 long 和 double 变量。其讨厌的结果是一个线程的更改可能更改某个变量,而第二个线程可能再次改变同一个变量。请考虑对那些变量类型进行同步赋值。
永不使用 stop() 方法。实际上,Java 2 中反对该方法。它会立即停止进程,但又不进行整理,这会导致许多问题,包括死锁和内存锁定。 应始终通过从 run() 方法返回来终止线程。
不要重新启动已停止的线程。run() 方法没有被调用,而 isAlive() 方法报告错误,指出实际上线程已死。
不要独占 CPU。如果程序的一部分独占了 CPU,就应该运行 yield() 方法,此方法可以让其它线程也有机会运行。请参阅这个小示例:
double answer = 0;
for (int i=0; i10000; i++) {
for (int j = 0; i10000; j++) {
answer = ((answer * i) + j) / j;
}
Thread.yield(); // Now other threads may run while this
//runs in the background
}