简体   繁体   English

不可能(?):ConcurrentLinkedQueue.size()上的NullPointerException

[英]Impossible (?): NullPointerException on ConcurrentLinkedQueue.size()

I'm getting this NPE on IBM JVM, 1.6 : 我在IBM JVM上获得了这个NPE, 1.6

java.lang. java.lang中。 NullPointerException at java.util.concurrent.ConcurrentLinkedQueue.first( ConcurrentLinkedQueue .java: 274 ) at java.util.concurrent.ConcurrentLinkedQueue.size(ConcurrentLinkedQueue.java:315) . java.util.concurrent.ConcurrentLinkedQueue.size上的java.util.concurrent.ConcurrentLinkedQueue.first( ConcurrentLinkedQueue .java: 274 )中的NullPointerException (ConcurrentLinkedQueue.java:315)。 . .

Relevant source shows that line #274 throws on a null " head " member. 相关的来源显示第274行抛出一个空的“ ”成员。 Search for usages shows this member is set to a new node() as needed, but is never nullified. 搜索用法显示此成员根据需要设置为新节点(),但永远不会无效。

How could that be? 怎么会这样? What am I missing? 我错过了什么?

... I cannot reproduce this when in debug mode. ...在调试模式下,我无法重现这一点。 This queue is accessed from multiple threads. 可以从多个线程访问此队列。

Snippet (Sun&IBM sources identical except for comments that change line numbers a bit): 片段(除了更改行号的注释之外,Sun和IBM的来源相同):

     Node<E> first() {
                for (;;) {
                    Node<E> h = head;
                    Node<E> t = tail;
                    Node<E> first = h.getNext(); // line #274 on IBM, #263 on Sun
...
    }
    }

Errors like that often come from the JIT compiler which gets some arcane optimization wrong. 像这样的错误通常来自JIT编译器,它会使一些神秘的优化错误。

There is little you can do; 你几乎无能为力; log error with IBM, they will then guide you through the process how to collect enough information for them to debug the issue. 与IBM的日志错误,他们将指导您完成如何收集足够的信息以调试问题的过程。

Note: In the last years, we filed two such issues. 注意:在过去几年中,我们提交了两个这样的问题。 So they aren't that uncommon even taking into account the enormous testing effort that IBM spends on their VM . 因此,即使考虑到IBM在其VM上花费巨大测试工作,它们也并非罕见。

One obscure scenario that could cause this: 一个不起眼的场景可能导致这种情况:

An class A holds a static queue that could take a while to be initialised. A类包含一个静态队列,可能需要一段时间才能初始化。

class A {
  // Long process which makes a second thread access `q` while it is still being constructed.
  public Object o = aLongProcess();
  public static Queue q = new ConcurrentLinkedQueue<String>();

A class B accesses the queue. B类访问队列。

class B {
  ...
  void doSomething () {
    String s = A.q.first();
  }

Thread T1 is first to access class A - thus beginning its initialisation process. 线程T1首先访问A类 - 从而开始其初始化过程。

Object o = A.o;

Thread T2 accesses the queue while it is still being initialised. 线程T2在仍处于初始化状态时访问队列。

B b = new B();
b.doSomething();

So basically you are looking at a race condition between two threads where one thinks the Queue is initialised while the other is still making it. 所以基本上你是在看两个线程之间的竞争条件,其中一个人认为队列被初始化而另一个人仍在制造它。

Note that just because the object name starts with Concurrent does not mean all functionality of the object is thread safe. 请注意,仅仅因为对象名称以Concurrent开头并不意味着对象的所有功能都是线程安全的。

I suppose another alternative could be that you only hold weak references to the queue and you are attempting to access it after it has been GC'd but I would expect you would mention that in your question. 我想另一种选择可能是你只保留对队列的弱引用,并且你试图在GC之后访问它,但我希望你会在你的问题中提到它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM