自动检测并行 Java 程序中的错误
文档选项
自动检测并行 Java 程序中的错误 自动检测并行 Java 程序中的错误

打印本页

自动检测并行 Java 程序中的错误 自动检测并行 Java 程序中的错误

将此页作为电子邮件发送

自动检测并行 Java 程序中的错误 自动检测并行 Java 程序中的错误

样例代码


级别: 中级

齐 尧 (qiyaoj@cn.ibm.com), 软件工程师, IBM 中国软件开发中心
甘 志 (ganzhi@cn.ibm.com), 高级软件工程师, IBM 中国软件开发中心
罗 志达 (luozd@cn.ibm.com), 软件工程师, IBM 中国软件开发中心
戴 晓君 (daixiaoj@cn.ibm.com), 软件工程师, IBM 中国软件开发中心

2008 年 10 月 17 日

并行程序的不确定性造成并行程序的错误很难查找,重现和调试。MTRAT 可以收集程序的运行时信息,在线分析程序中所有可能的并行程序错误。

当 CPU 进入多核时代之后,并行编程将更加流行,但是编写并行程序更容易出错。在开发过程中,工程师能注意到同一个程序在单线程运行时是正确的,但是在多线程时,它会有可能出错。和并行相关的错误的产生原因通常都非常隐晦,而且在一次测试中,它们的出现与否具有很强的随机性。由于程序中多个线程之间可能以任意的方式交错执行,即使一个并行程序正确的运行了成百上千次,下一次运行仍然可能出现新的错误。

Multi-Thread Run-time Analysis Tool 是由 IBM 为多线程 Java 程序开发的运行时分析工具,它可用于分析并查找 Java 代码中的一些不容易发现的潜在并行程序错误,比如数据竞争 (Data Race) 和死锁 (Deadlock),从而提高并行程序的代码质量。本文将介绍检测 Java 程序中随机并行错误的一种新工具 (http://alphaworks.ibm.com/tech/mtrat),检查 Java 代码中的潜在的并行程序错误,从而提高代码的安全性和稳定性,并演示其对于潜在而并未发生的错误的发掘能力。

概述

Java 编程语言为编写多线程应用程序提供强大的语言支持。但是,编写有用的、没有错误的多线程程序仍然比较困难。编程语言中线程面临很多挑战。在这些挑战中,最主要的就是编程复杂度的提高。这些编程复杂度是由同步共享变量的访问,潜在的依赖于时序的错误和调试和优化并行程序的复杂性造成的。

MTRAT 只所以把不同的技术集成到了一个单一的开发工具中,是为了掩盖工具内部的复杂性,并使得 MTRAT 方便使用。 MTRAT 主要由以下部分组成,

  • 简单的命令行界面和 Eclipse 插件。输出 MTRAT 检查到的并行错误。
  • 动态的 Java 字节码修改引擎。可以在 Java 类文件被 Java 虚拟机加载的时候,修改 Java 类。
  • 程序运行时信息收集器。收集程序的动态信息,比如内存访问,线程同步,创建和结束。
  • 高效的运行时分析引擎。收集到的运行时信息会被在线分析,如果发现潜在的并行错误,将会通过界面报告给用户。

自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
回页首


检测数据竞争

在并行程序中,当两个并行的线程,在没有任何约束的情况下,访问一个共享变量或者共享对象的一个域,而且至少要有一个操作是写操作,就会发生数据竞争错误。MTRAT 最强大的功能就是发现并行程序中潜在的数据竞争错误。在下边的 Java 程序就隐藏了一个潜在的数据竞争错误。

 package sample;
                        class Value
                        {
                        private int x;
                        public Value()
                        {
                        x = 0;
                        }
                        public synchronized void add (Value v)
                        {
                        x = x + v.get();
                        }
                        public int get() {return x;}
                        }
                        class Task extends Thread
                        {
                        Value v1, v2;
                        public Task (Value v1, Value v2)
                        {
                        this.v1 = v1;
                        this.v2 = v2;
                        }
                        public void run() {v1.add(v2);}
                        }
                        public class DataRace
                        {
                        public static void main (String[] args) throws InterruptedException
                        {
                        Value v1 = new Value ();
                        Value v2 = new Value ();
                        Thread t1 = new Task(v1, v2);
                        Thread t2 = new Task (v2, v1);
                        t1.start();
                        t2.start();
                        t1.join();
                        t2.join();
                        }
                        }

Value声明一个整形域x,一个同步方法add修改这个域,和一个方法get返回域x的值。类Task以两个类Value的实例来构造。

以 MTRAT 运行类sample.DataRace,可以在运行时刻检查程序中的潜在的数据竞争错误

 $ mtrat -cp . sample.DataRace
                        Data Race 1 : 3 : sample/Value : x
                        Thread Thread-3 id: 7 : WRITE
                        sample.Value : get : 15
                        sample.Value : add : 15
                        sample.Task : run : 32
                        Thread Thread-4 id: 8 : READ
                        sample.Value : get : 18
                        sample.Value : add : 15
                        sample.Task : run : 32
                        Data Race 2 : 4 : sample/Value : x
                        Thread Thread-3 id: 7 : READ
                        sample.Value : get : 15
                        sample.Value : add : 15
                        sample.Task : run : 32
                        Thread Thread-4 id: 8 : WRITE
                        sample.Value : get : 15
                        sample.Value : add : 15
                        sample.Task : run : 32

在图形界面Eclipse中运行,得到以下结果:


Figure 1.
自动检测并行 Java 程序中的错误

MTRAT 报告出了两个数据竞争错误,因为类Task的两个实例会访问类Value的对象,然而这个共享的对象却没有被一个共同的锁保护。

例如,在并行程序执行过程中,可能存在这样的时刻,一个线程执行方法get读域x的值,而另外一个线程执行执行方法add写域x

根据检查结果,MTRAT 发现了两个数据竞争错误,在类sample/Valuex。程序员在得到这两个数据竞争错误后,很容易就能发现程序中存在两个线程并发访问同一个对象域的可能。如果两个线程可以顺序访问这个对象域,这两个数据竞争问题就可以被消除了。


自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
回页首


检测死锁

死锁问题也是并行 Java 程序中常见的问题。在 Java 程序中出现死锁,是因为 synchronized 关键字会造成运行的线程等待关联到某个一个对象上的锁。由于线程可能已经获得了别的锁,两个线程就有可能等待对方释放掉锁。在这种情况下,两个线程将永远等待下去。

在下边的 Java 程序就隐藏了一个潜在死锁问题,

 class T3 extends Thread
                        {
                        StringBuffer L1;
                        StringBuffer L2;
                        public T3(StringBuffer L1, StringBuffer L2)
                        {
                        this.L1 = L1;
                        this.L2 = L2;
                        }
                        public void run()
                        {
                        synchronized (L1)
                        {
                        synchronized (L2)
                        {
                        }
                        }
                        }
                        }
                        public class Deadlock
                        {
                        void harness2() throws InterruptedException
                        {
                        StringBuffer L1 = new StringBuffer("L1");
                        StringBuffer L2 = new StringBuffer("L2");
                        Thread t1 = new T3(L1, L2);
                        Thread t2 = new T3(L2, L1);
                        t1.start();
                        t2.start();
                        t1.join();
                        t2.join();
                        }
                        public static void main(String[] args) throws InterruptedException
                        {
                        Deadlock dlt = new Deadlock();
                        dlt.harness2();
                        }
                        }

在类 Deadlockharness2 方法中,类 Deadlock 的两个实例被创建,作为参数传递到类 T3 的构造函数中。在类 T3run 方法中,线程会依次获得这个两个对象的锁,然后以相反的顺序释放这两个锁。由于两个 StringBuffer 实例以不同的顺序传递给类 T3,两个线程会以不同的顺序获得这两个锁。这样,死锁就出现了。

以 MTRAT 运行类 sample.Deadlock,可以在运行时刻检查程序中的潜在的死锁错误:

 $ mtrat -Dcom.ibm.mtrat.deadlock=true  -cp . sample.Deadlock
                        Thread 7 : Acquire L1 L2
                        Dead Lock 1
                        Thread 7, acquired lock1 -> try lock2  sample/T3  line 109
                        Thread 8, acquired lock2 -> try lock1  sample/T3  line 109
                        Thread 8 : Acquire L2 L1

在图形界面Eclipse中运行,得到以下结果:


图 2.
自动检测并行 Java 程序中的错误

在 MTRAT 的死锁检查报告中我们可以发现,线程 Thread 7 已经获得了锁 lock1,在程序 109 行试图获得锁 lock2。然而,线程 Thread 8 已经获得了锁 lock2,在程序 109 行试图获得锁lock1

根据 MTRAT 的死锁检查报告,程序员可以很容易得知道,这个死锁问题是由于两个线程按照相反的顺序上锁造成的。避免这种问题的一种方法是让代码按固定的全局顺序获取锁。那么如果两个线程按照一致的顺序去上锁,死锁错误就可以被消除了。

 void harness2() throws InterruptedException
                        {
                        StringBuffer L1 = new StringBuffer("L1");
                        StringBuffer L2 = new StringBuffer("L2");
                        Thread t1 = new T3(L1, L2);
                        Thread t2 = new T3(L1, L2);
                        t1.start();
                        t2.start();
                        t1.join();
                        t2.join();
                        }


自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
回页首


结束语

在本文中,我们展示了如何检查并行 Java 程序中潜在的错误,比如数据竞争和死锁。通过使用 MTRAT,您可以在程序开发阶段发现用肉眼难以发现的并行程序错误。该工具使开发正确和高质量的并行程序变得更加容易。



自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误
回页首


下载

描述 名字 大小 下载方法
示例代码 src.zip 12 KB HTTP
自动检测并行 Java 程序中的错误
自动检测并行 Java 程序中的错误 关于下载方法的信息 自动检测并行 Java 程序中的错误


参考资料



作者简介

自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

齐尧,IBM 中国软件实验室(CSDL BJ)China Emerging Technology Institute 成员,主要研究方向为程序动态分析和程序静态分析。他在北京理工大学获得硕士学位。您可以通过 qiyao@cn.ibm.com 联系他。


自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

甘志,IBM 中国软件实验室(CSDL BJ)SOA Design Center 成员,主要研究方向为 SOA 和 Security,他还对羽毛球运动很感兴趣。他在上海交通大学计算机系攻读网络安全方向博士学位,期间发表了多篇论文和技术书籍。您可以通过 ganzhi@cn.ibm.com 联系他。


自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

罗志达,IBM 中国软件实验室(CSDL BJ)China Emerging Technology Institute 成员,主要研究方向为程序动态分析和程序静态分析。他在北京大学获得软件工程硕士学位。您可以通过 luozd@cn.ibm.com 联系他。


自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

自动检测并行 Java 程序中的错误

戴晓君,IBM 中国软件实验室(CSDL BJ)China Emerging Technology Institute 成员,主要研究方向为并行编程和敏捷软件开发方法。他在中国科学院软件研究所获得计算机

相关文章:

  • 2022-02-18
  • 2021-09-24
  • 2022-12-23
  • 2021-07-12
  • 2021-09-29
  • 2021-11-30
  • 2021-06-28
猜你喜欢
  • 2021-11-14
  • 2022-03-06
  • 2022-01-25
  • 2022-01-19
  • 2021-08-09
  • 2022-12-23
相关资源
相似解决方案