Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unusual Java behavior - why does this work?

I've found some interesting behavior... I can't decide if it's a bug or incompetence, but currently leaning towards incompetence.

This code will not enter the loop, even if there are messages waiting:

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

This code DOES enter the loop, notice the null assignment:

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

This code is running on Glassfish 3.1.1b10 HotSpot 1.6_26 on Windows 32bit. I can't think of an explanation why the first block doesn't work!

EDIT/UPDATE July 13, 2011:

First, I began stopping the Glassfish domain and deleting it between deploys per request, and this still occurs :)

Second, I cannot sync on Destination or Consumer, as this is Java EE code. But, I can assure there are messages available. There's about 500 of them available an no consumers. In fact, creating a QueueBrowser tells me there's messages available!

Third, this program prints "WORKS!" every time!!! ARGH!!!

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();
}

Lastly, I was speaking about my own incompetence. ;)

like image 323
Jonathan S. Fisher Avatar asked Jul 12 '11 22:07

Jonathan S. Fisher


2 Answers

As Ryan said, seems like a race condition. The bytecodes for both codes are the same, with the exception of an extra "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

}

If you want to test this theory, try this code:

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

It's a noop, but the bytecode is almost the same as the second code (changes "astore_0" to "astore_1").

BTW, I had horrible results with "receiveNoWait". I prefer "receive(smallTimeout)", to avoid buffer underruns or such.

like image 93
Eduardo Costa Avatar answered Nov 15 '22 19:11

Eduardo Costa


This sounds like a race condition to me. Declaration of objects without instantiation will always result in null values. You may believe there are Messages waiting for you in the first case, but I bet there aren't. Before the conditional loop, print out the number of objects there are and verify the resulting behavior. If you are in a multi-threaded situation, synchronize on the Message queue if necessary to facilitate this. I am betting it works exactly as expected.

like image 27
Ryan Avatar answered Nov 15 '22 17:11

Ryan