IDEA中的并发调试

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方法上:

IDEA中的并发调试
avatar

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

IDEA中的并发调试
avatar

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

    
使用IDEA可以很好的实现这点,右击断点,条件中排除掉主线程main:

IDEA中的并发调试
avatar

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

IDEA中的并发调试
avatar

    
上图中可看到t1和t2都在RUNNING状态,t3处于SLEEPING状态。但控制台中”t3 is run!”一直在不停的输出,说明t3线程并不会因为t1和t2断点而进入挂起模式。

    
当前选中的是t1线程,如果进行单步执行,t1会往下执行,而t2不会往下执行。如果要操作t2往下执行可以切换为t2线程。

挂起整个虚拟机

    
默认情况下,当断点条件成立时,系统会挂起相关的线程,没有断点的线程会继续执行。为了排除其他线程可能对整个调试产生不利的影响,我们可设置断点类型为挂起整个虚拟机,而不是挂起相关线程。操作如下:

IDEA中的并发调试
avatar

    
选择”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,断点设置如下:

IDEA中的并发调试
avatar

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

IDEA中的并发调试
avatar

    
接着,切换t1线程进行赋值成功,然后切换t2线程赋值,得到异常”java.lang.ArrayIndexOutOfBoundsException”。


本篇文章来源于微信公众号: 冥oo迹

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/10813.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!