记录一个System.out.println导致Eclipse主线程阻塞的 bug

简介: 昨天接到市场的一个bug 在生成xml文件的时候整个Eclipse RCP卡死了   提到xml就不得不想到著名的“GFW导致DTD访问无能而Dom4j做DTD校验如果发现有网络能通会一直等到超时所以超级慢”的bug。

昨天接到市场的一个bug

在生成xml文件的时候整个Eclipse RCP卡死了

 

提到xml就不得不想到著名的“GFW导致DTD访问无能而Dom4j做DTD校验如果发现有网络能通会一直等到超时所以超级慢”的bug。
当然,最后排查发现和这个bug无关,这个bug解决方案:
1、保持断网状态
2、保持FQ状态
3、本地化DTD
4、使用国内镜像DTD
View Code

 

使用jconsole对线程进行检查,发现主线程卡在了System.out.println

原因是锁被另外一个线程持有,如果连System.out.println都要卡住,这还怎么写代码?

经过一番排查,发现了问题所在。

首先来看看System.out.println(String)的源码:

    public void println(String arg0) {
        synchronized (this) {
            this.print(arg0);
            this.newLine();
        }
    }

 

可以注意到,同步语句块的对象监视器是该PrintStream自身。

在Eclipse RCP环境中,System.out进行了转发,执行具体操作的是IOConsoleOutputStream对象。

最终,会落到IOConsolePartitioner.streamAppended来处理,关键源码如下:

public void streamAppended(IOConsoleOutputStream stream, String s) throws IOException {
        ...synchronized(pendingPartitions) {
           ...if (fBuffer > 160000) {
                if(Display.getCurrent() == null){
                    try {
                        pendingPartitions.wait();
                    } catch (InterruptedException e) {
                    }
                } else {
                    processQueue();
                }
            }
        }
    }

 

Display.getCurrent==null为true时,当前线程为非UI线程,反之则是UI线程。

fBuffer则是同一个线程不间断执行steamAppended时录入的字符串长度。

processQueue则类似于flush,用于把buffer展示到console上,然后归零计数器,并且执行pendingPartitions.notifyAll()。

 

这一串儿代码的意思是说:

1、如果某个线程不间断录入了超过16万个字符,则判断它是不是UI线程

2、如果是UI线程,则直接输出,并且唤醒其他阻塞的非UI线程

3、如果不是UI线程,则阻塞

 

看上去似乎挺好的,但是结合起来我们会发现几个问题:

1、如果一个非UI线程执行了System.out.println(String),比如输出长篇小说,抱歉,会卡死,除非过程中有个UI线程帮助输出;

2、如果一个非UI线程已经录入了16万个字符导致自己wait,所有调用steamAppended的外层同步锁都会死锁。

这是因为,wait仅仅只是释放了当前锁,也就是之前源码中对象监视器pendingPartitions监视的同步语句块

而System.out.println是由PrintStream监视的,锁依然被该非UI线程持有,就算UI线程打算通过out.println来触发processQueue的notifyAll,也不可能办到了。

 

在当前,我遇到的问题是eclipse的常见svn插件subclipse在执行members来遍历svn resource时,会检查resource.isIgnore,这个过程如果出现了版本不匹配一类的错误,会直接输出log,这个log,由于members是个无阻塞的树遍历,有可能超出16万字符串。

我目前的解决方式是换subversion插件,当然,可以尝试在UI线程中处理svn log。

但是,eclipse RCP的这个bug至少,至今还是存在的。

 

目录
相关文章
|
IDE 应用服务中间件 程序员
如何删除 eclipse 中多余的 Tomcat server?为什么产生这种 bug?
如何删除 eclipse 中多余的 Tomcat server?为什么产生这种 bug?
302 0
如何删除 eclipse 中多余的 Tomcat server?为什么产生这种 bug?
|
Oracle Java 关系型数据库
Eclipse解决bug:JDK是8.0,打不开eclipse
Eclipse解决bug:JDK是8.0,打不开eclipse
327 0
Eclipse解决bug:JDK是8.0,打不开eclipse
|
缓存 Java 编译器
可能出现jdk版本的bug和eclipse修改项目jdk的版本具体步骤
可能出现jdk版本的bug和eclipse修改项目jdk的版本具体步骤
170 0
可能出现jdk版本的bug和eclipse修改项目jdk的版本具体步骤
|
Android开发
解决bug:在eclipse中导入Android项目时报错: “invalid resource directory name bin/res/crunch”
解决bug:在eclipse中导入Android项目时报错: “invalid resource directory name bin/res/crunch”
518 0
|
Java 应用服务中间件 Android开发
导入eclipse里面java项目出现的bug总结
导入eclipse里面java项目出现的bug总结
142 0
导入eclipse里面java项目出现的bug总结
|
Java Maven Android开发
|
开发工具 Android开发
我的Android进阶之旅------>解决Bug:打开eclipse报错,发现了以元素 'd:skin' 开头的无效内容。此处不应含有子元素。
今天来打开Eclipse 报错了,错误信息如下: [2015-08-01 09:07:43 - Android SDK] Error when loading the SDK: Error: Error parsing D:\Android\Eclipse\sdk\system-images\android-22\android-wear\armeabi- v7a\devices.xml cvc-complex-type.2.4.d: 发现了以元素 'd:skin' 开头的无效内容。
1088 0
|
Java Android开发 监控
Eclipse调试Bug的七种常用技巧(转)
    注意事项及小结: (1)Line Breakpoint:如果设置Conditional,监控的变量需要比当前行高一级block,譬如for(int i=0;i
1413 0