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

  1. 云栖社区>
  2. 博客>
  3. 正文

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

anrainie 2017-11-23 10:27:00 浏览764
展开阅读全文

昨天接到市场的一个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至少,至今还是存在的。

 

网友评论

登录后评论
0/500
评论
anrainie
+ 关注