【问题标题】:Unusual Java behavior - why does this work?不寻常的 Java 行为 - 为什么会这样?
【发布时间】:2011-10-04 01:23:14
【问题描述】:

我发现了一些有趣的行为...我无法确定这是错误还是无能,但目前倾向于无能。

这段代码不会进入循环,即使有消息在等待:

Message msg;
while ((msg = consumer.receiveNoWait()) != null) {
    System.out.println(msg);
}

这段代码确实进入了循环,注意空赋值:

Message msg = null;
while ((msg = consumer.receiveNoWait()) != null) {
    System.out.println(msg);
}

此代码在 Windows 32 位的 Glassfish 3.1.1b10 HotSpot 1.6_26 上运行。我想不出为什么第一个块不起作用的解释!

2011 年 7 月 13 日编辑/更新:

首先,我开始停止 Glassfish 域并在每次请求的部署之间删除它,这仍然会发生 :)

其次,我无法在 Destination 或 Consumer 上同步,因为这是 Java EE 代码。但是,我可以保证有可用的消息。其中大约有 500 个可用且没有消费者。事实上,创建 QueueBrowser 会告诉我有可用的消息!

第三,这个程序打印“WORKS!”每次!!!啊!!!

public static void main(String[] args) {
    Object obj;

    if ((obj = getNotNull()) != null) {
        System.out.println("worked!");
    } else {
        System.out.println("failed!");
    }
}

static Object getNotNull() {
    return new Object();
}

最后,我说的是我自己的无能。 ;)

【问题讨论】:

  • 这取决于consumer.receiveNoWait()。试试System.out.println(consumer.receiveNoWait() == null)
  • 对任何过分热心的模组的网站注释:这确实(我重复:)属于CGSE。以防万一您因为“挑战”一词而考虑迁移。代表 CGSE 用户感谢您。
  • 如果您认为这很糟糕,我可以让来自 Microsoft 的 Java 虚拟机的 JIT 抛出一个不可能的空指针异常。 (Java的VM本身没有出现bug,证明是JIT的错。)
  • 代码相同,问题在于receiveNoWait() 以及队列中任何消息的可用性。很简单:如果轮询一个队列并且它是空的,效果将完全一样。
  • @edutesoy 谁的无能* ;)

标签: java jakarta-ee jms


【解决方案1】:

对我来说,这听起来像是一种竞争条件。没有实例化的对象声明将始终导致空值。您可能认为在第一种情况下有消息在等着您,但我敢打赌没有。在条件循环之前,打印出对象的数量并验证结果行为。如果您处于多线程情况,请在必要时在消息队列上同步以促进此操作。我敢打赌它会完全按预期工作。

【讨论】:

  • 其实我相信它只是一个持久队列,数据是在'receiveNoWait'之后插入的,所以下次它可用。
  • @bestsss 这并没有解释为什么它只在变量用“null”初始化时才有效
【解决方案2】:

我的钱是你没有运行你认为的代码。您提到“在 Glassfish 3.1.1b10 上运行”,因此单元测试的方式可能并不多,因此查明错误所在的能力变得更加困难。

在一些奇怪的边缘情况下,有几种可能性可能会有所不同。

  • 它对类加载顺序进行了细微的更改,并且您在静态初始化中做了一些狡猾的事情。我什至不确定它是否会对类加载顺序产生影响。
  • null 赋值会破坏堆栈上的引用,这允许对引用与弱/软/幻象/终结器引用一起使用的对象的对象进行垃圾收集,或者可能只是更改内存分配时间和扰乱比赛条件。

【讨论】:

  • 我认为这是一个不错的解释,但我相当肯定我已经涵盖了这一点。可以肯定的是,我将在尝试两段代码之间删除 glassfish 域,这将 100% 保证部署之间没有串扰。我会告诉你我发现了什么。
  • 删除域后仍然存在!
【解决方案3】:

您是否可以提供消费者 oject 可能是什么以及 receiveNoWait() 可能是什么?

【讨论】:

【解决方案4】:

正如 Ryan 所说,这似乎是一种竞赛条件。两个代码的字节码是相同的,除了一个额外的“astore”:

public static void code1()   throws javax.jms.JMSException;
  Code:
   0:   getstatic   #2; //Field consumer:Ljavax/jms/MessageConsumer;
   3:   invokeinterface #3,  1; //InterfaceMethod javax/jms/MessageConsumer.receiveNoWait:()Ljavax/jms/Message;
   8:   dup
   9:   astore_0
   10:  ifnull  23
   13:  getstatic   #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   16:  aload_0
   17:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   20:  goto    0
   23:  return

public static void code2()   throws javax.jms.JMSException;
  Code:
   0:   aconst_null
   1:   astore_0
   2:   getstatic   #2; //Field consumer:Ljavax/jms/MessageConsumer;
   5:   invokeinterface #3,  1; //InterfaceMethod javax/jms/MessageConsumer.receiveNoWait:()Ljavax/jms/Message;
   10:  dup
   11:  astore_0
   12:  ifnull  25
   15:  getstatic   #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   18:  aload_0
   19:  invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   22:  goto    2
   25:  return

}

如果你想测试这个理论,试试这个代码:

Message msg;
String dummy = null;
while ((msg = consumer.receiveNoWait()) != null) {
    System.out.println(msg);
}

这是一个noop,但字节码与第二个代码几乎相同(将“astore_0”更改为“astore_1”)。

顺便说一句,“receiveNoWait”的结果很糟糕。我更喜欢“receive(smallTimeout)”,以避免缓冲区不足等。

【讨论】:

  • 我在完整的代码中使用 javap 得到了同样的效果!!!当将代码部署到 glassfish 时,代码发生了一些奇怪的事情,并且出现了某种竞赛。因为我不能反汇编存根代码......我想我永远不会知道!但是,我会给你回答的分数,因为你是最接近的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多