简体   繁体   中英

Strange behaviour arrayBlockingQueue with array elements

I am having some strange behavior with the use of an ArrayBlockingQueue which I use in order to communicate between certain treads in a java application.

I am using 1 static ArrayBlockingQueue as initialised like this:

protected static BlockingQueue<long[]> commandQueue;

Followed by the constructor which has this as one of its lines:

commandQueue = new ArrayBlockingQueue<long[]>(amountOfThreads*4);

Where amountOfThreads is given as a constructor argument.

I then have a producer that creates an array of long[2] gives it some values and then offers it to the queue, I then change one of the values of the array directly after it and offer it once again to the queue:

long[] temp = new long[2];
temp[0] = currentThread().getId();
temp[1] = gyrAddress;//Address of an i2c sensor
CommunicationThread.commandQueue.offer(temp);//CommunicationThread is where the commandqueue is located
temp[1] = axlAddress;//Change the address to a different sensor
CommunicationThread.commandQueue.offer(temp);

The consumer will then take this data and open up an i2c connection to a specific sensor, get some data from said sensor and communicate the data back using another queue. For now however I have set the consumer to just consume the head and print the data.

long[] command = commandQueue.take();//This will hold the program until there is at least 1 command in the queue
if (command.length!=2){
    throw new ArrayIndexOutOfBoundsException("The command given is of incorrect format");
}else{
    System.out.println("The thread with thread id " + command[0] + " has given the command to get data from address " +Long.toHexString(command[1]));
}

Now for testing I have a producer thread with these addresses (byte) 0x34, (byte)0x44 If things are going correctly my output should be:

The thread with thread id 14 has given the command to get data from address 44
The thread with thread id 14 has given the command to get data from address 34

However I get:

The thread with thread id 14 has given the command to get data from address 34
The thread with thread id 14 has given the command to get data from address 34

Which would mean that it is sending the temp array after it has changed it.

Things that I did to try and fix it: I tried a sleep, if I added a 150 ms sleep then the response is correct. However this method will quite obviously affect performance... Since the offer method returns a true I tried the following piece of code

boolean tempBool = false;
while(!tempBool){
    tempBool = CommunicationThread.commandQueue.offer(temp);
    System.out.println(tempBool);
}

Which prints out a true. This did not have an affect.

I tried printing temp[1] after this while loop and at that moment it is the correct value.(It prints out 44 however the consumer receives 34 )

What most likely is the case is a syncronisation issue, however I thought that the point of a BlockingQueue based object would be to solve this.

Any help or suggestion on the workings of this BlockingQueue would be greatly appreciated. Let me end on a note that this is my first time working with queues in between threads in java and that the final program will be running on a raspberry pi using the pi4j library to communicate with the sensors

Since you asked about how BlockingQueue works exactly, let's start with that:

A blocking queue is a queue that blocks when you try to dequeue from it while the queue is empty, or when you try to enqueue items to it while the queue is already full. A thread trying to dequeue from an empty queue is blocked until some other thread inserts an item into the queue.

Soo these blocking queue's prevent different threads from reading/writing to a queue while it is not yet possible because it is either empty or full.

As Andy Turner and JB Nizet already explained, variables are statically shared in memory. This means that when your thread that reads the queue it finds a reference (AKA a pointer) to this variable (in memory) and uses this pointer in it's following code. However before it manages to read this data, you already changed the variable, normally in non-threaded applications this wouldn't be an issue since only one thread will try to read from memory and it will always be executed chronologically. A way to circumvent this is to create a new variable/array (which will assign itself to new memory) with the variable data every time you add an entry to the queue, this way you make sure you do not overwrite a variable in memory before it is processed by the other thread. A simple way to do this is:

long[] tempGyr = new long[2];
tempGyr[0] = currentThread().getId();
tempGyr[1] = gyrAddress;
CommunicationThread.commandQueue.offer(tempGyr);//CommunicationThread is where the commandqueue is located

long[] tempAxl = new long[2];
tempAxl[0] = currentThread().getId();
tempAxl[1] = axlAddress;
CommunicationThread.commandQueue.offer(tempAxl);

Hope this explains the subject, if not: feel free to ask for additional questions :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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