启天论坛软件硬件技术Java技术 → 自动检测并行 Java 程序中的错误


  共有330人关注过本帖树形打印

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

帅哥,在线噢!
junsan
  1楼 个性首页 | QQ | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 总掌门
等级:管理员 帖子:11764 积分:7955 威望:757 精华:24 注册:2005-7-14 15:13:00
自动检测并行 Java 程序中的错误  发帖心情 Post By:2008-11-3 14:34:00



并行程序的不确定性造成并行程序的错误很难查找,重现和调试。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 类。
程序运行时信息收集器。收集程序的动态信息,比如内存访问,线程同步,创建和结束。
高效的运行时分析引擎。收集到的运行时信息会被在线分析,如果发现潜在的并行错误,将会通过界面报告给用户。

检测数据竞争

在并行程序中,当两个并行的线程,在没有任何约束的情况下,访问一个共享变量或者共享对象的一个域,而且至少要有一个操作是写操作,就会发生数据竞争错误。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();
     }
}


主页:http://www.rsky.com.cn

培训论坛:http://www.javastar.org

Blog:http://blog.csdn.net/junnef

技术交流QQ:334620162

培训咨询QQ:66883074

合租咨询QQ:631066985

MSN:junsanjin@hotmail.com

Emial:junsanjin@263.net

传真:(010)950507转747393
启天合组-10人 15人 20人 多人合租空间 支持(0中立(0反对(0回到顶部
帅哥,在线噢!
junsan
  2楼 个性首页 | QQ | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 总掌门
等级:管理员 帖子:11764 积分:7955 威望:757 精华:24 注册:2005-7-14 15:13:00
  发帖心情 Post By:2008-11-3 14:35:00

类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中运行,得到以下结果:
图片点击可在新窗口打开查看
MTRAT 报告出了两个数据竞争错误,因为类Task的两个实例会访问类Value的对象,然而这个共享的对象却没有被一个共同的锁保护。

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

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


主页:http://www.rsky.com.cn

培训论坛:http://www.javastar.org

Blog:http://blog.csdn.net/junnef

技术交流QQ:334620162

培训咨询QQ:66883074

合租咨询QQ:631066985

MSN:junsanjin@hotmail.com

Emial:junsanjin@263.net

传真:(010)950507转747393
启天合组-10人 15人 20人 多人合租空间 支持(0中立(0反对(0回到顶部
帅哥,在线噢!
junsan
  3楼 个性首页 | QQ | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 总掌门
等级:管理员 帖子:11764 积分:7955 威望:757 精华:24 注册:2005-7-14 15:13:00
  发帖心情 Post By:2008-11-3 14:35:00

检测死锁

死锁问题也是并行 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();
     }

}

在类 Deadlock 的 harness2 方法中,类 Deadlock 的两个实例被创建,作为参数传递到类 T3 的构造函数中。在类 T3 的 run 方法中,线程会依次获得这个两个对象的锁,然后以相反的顺序释放这两个锁。由于两个 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中运行,得到以下结果:
图片点击可在新窗口打开查看
在 MTRAT 的死锁检查报告中我们可以发现,线程 Thread 7 已经获得了锁 lock1,在程序 109 行试图获得锁 lock2。然而,线程 Thread 8 已经获得了锁 lock2,在程序 109 行试图获得锁lock1。


主页:http://www.rsky.com.cn

培训论坛:http://www.javastar.org

Blog:http://blog.csdn.net/junnef

技术交流QQ:334620162

培训咨询QQ:66883074

合租咨询QQ:631066985

MSN:junsanjin@hotmail.com

Emial:junsanjin@263.net

传真:(010)950507转747393
启天合组-专业合租空间提供商 支持(0中立(0反对(0回到顶部
帅哥,在线噢!
junsan
  4楼 个性首页 | QQ | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 总掌门
等级:管理员 帖子:11764 积分:7955 威望:757 精华:24 注册:2005-7-14 15:13:00
  发帖心情 Post By:2008-11-3 14:35:00

在 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 程序中潜在的错误,比如数据竞争和死锁。通过使用 MTRAT,您可以在程序开发阶段发现用肉眼难以发现的并行程序错误。该工具使开发正确和高质量的并行程序变得更加容易。

links: http://www.ibm.com/developerworks/cn/java/j-lo-mtrat/


主页:http://www.rsky.com.cn

培训论坛:http://www.javastar.org

Blog:http://blog.csdn.net/junnef

技术交流QQ:334620162

培训咨询QQ:66883074

合租咨询QQ:631066985

MSN:junsanjin@hotmail.com

Emial:junsanjin@263.net

传真:(010)950507转747393
启天合组-专业合租空间提供商 支持(0中立(0反对(0回到顶部