IDEA中的并发调试
-
一、介绍
-
二、进入非主线程的调试
-
三、挂起整个虚拟机
-
四、重现错误
介绍
最近看葛一鸣了的”实战Java高并发程序设计”一书,里面有一章介绍了”并行程序调试”,不过书中是基于Eclipse编辑器的,这里总结一下IDEA中的调试方法,大同小异。
实验样本如下:
/**
* 两个线程都过了数组大小检查,先后插入数据时 引起 java.lang.ArrayIndexOutOfBoundsException
*/
public class UnsafeArrayList {
static ArrayList al=new ArrayList();
static class AddTask implements Runnable{
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
System.out.println( Thread.currentThread().getName() + " is run!");
for(int i=0;i<1000000;i++){
al.add(new Object());
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new AddTask(),"t1");
Thread t2=new Thread(new AddTask(),"t2");
t1.start();
t2.start();
Thread t3=new Thread(new Runnable(){
@Override
public void run() {
while(true){
try {
Thread.sleep(10000);
} catch (InterruptedException e) {}
System.out.println( Thread.currentThread().getName() + " is run!");
}
}
},"t3");
t3.start();
}
}
模拟多线程下操作ArrayList。t1和t2表示不同线程下操作同一个任务,t3是操作其他任务。
进入非主线程的调试
运行程序,抛出如下异常:
java.lang.ArrayIndexOutOfBoundsException: 28
at java.util.ArrayList.add(ArrayList.java:463)
at com.mm.mmblogs.b5.UnsafeArrayList$AddTask.run(UnsafeArrayList.java:17)
at java.lang.Thread.run(Thread.java:748)
找到错误ArrayList.add里,得知错误在如下代码:
elementData[size++] = e;
将断点设置在ensureCapacityInternal方法上:

以调试方式启动程序,出现的Debugger图如下:

上图中,可看到主线程main停留在了ArrayList.add()函数断点处,显示了完整的调用堆栈。但很不幸的是,其实我们对主函数并没有太大兴趣。我们更关心的是程序中t1和t2线程对ArrayList的调用。
使用IDEA可以很好的实现这点,右击断点,条件中排除掉主线程main:

再次以调试方式启动程序,就可调试线程t1和t2了,出现的Debugger图如下:

上图中可看到t1和t2都在RUNNING状态,t3处于SLEEPING状态。但控制台中”t3 is run!”一直在不停的输出,说明t3线程并不会因为t1和t2断点而进入挂起模式。
当前选中的是t1线程,如果进行单步执行,t1会往下执行,而t2不会往下执行。如果要操作t2往下执行可以切换为t2线程。
挂起整个虚拟机
默认情况下,当断点条件成立时,系统会挂起相关的线程,没有断点的线程会继续执行。为了排除其他线程可能对整个调试产生不利的影响,我们可设置断点类型为挂起整个虚拟机,而不是挂起相关线程。操作如下:

选择”Suspend All”即可挂起整个虚拟机,如果想所有的端点模式都是挂起虚拟机而不是挂起相关线程,可设置为默认”Make Default”。
挂起整个虚拟机后,再次调试程序,这次t3线程并没有输出”t3 is run!”,说明这次已经挂起整个虚拟机了。啥时候挂起相关线程,啥时候挂起整个虚拟机,需由实际场景决定。
重现错误
造成”java.lang.ArrayIndexOutOfBoundsException”的异常,很有可能在ArrayList容量快用完时(只有1个可用空间),如果两个线程同时进入add()函数,并同时判断认为系统满足继续添加元素而不需要扩容,那么两者都不会进行扩容操作。那么必然有一个线程会将数据写到边界外,从而产生ArrayArrayIndexOutOfBoundsException。
我们都知道,ArrayList默认容量是10。
private static final int DEFAULT_CAPACITY = 10;
进入add()函数后,模拟容量为9,断点设置如下:

然后切换t1和t2两个线程进行扩容检查,下图为t1进行扩容检查,t2同理:

接着,切换t1线程进行赋值成功,然后切换t2线程赋值,得到异常”java.lang.ArrayIndexOutOfBoundsException”。
本篇文章来源于微信公众号: 冥oo迹
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/10813.html