我使用ArrayBlockingQueue有一些奇怪的行为,我使用它来在java应用程序中的某些步骤之间进行通信。
我使用1个静态ArrayBlockingQueue作为初始化,如下所示:
protected static BlockingQueue<long[]> commandQueue;
后面是构造函数,它将其作为其中一行:
commandQueue = new ArrayBlockingQueue<long[]>(amountOfThreads*4);
其中amountOfThreads
作为构造函数参数给出。
然后我有一个生成器,它创建一个long[2]
数组,给它一些值然后将它提供给队列,然后我直接在它之后更改数组的一个值并再次提供给它队列:
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);
然后,消费者将获取此数据并打开与特定传感器的i2c连接,从所述传感器获取一些数据并使用另一个队列传回数据。 但是现在我已经将消费者设置为消耗头部并打印数据。
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]));
}
现在进行测试我有一个带有这些地址的生产者线程(byte) 0x34, (byte)0x44
如果事情正确,我的输出应该是:
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
但是我得到了:
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
这意味着它在更改后发送临时数组。
我尝试修复它的事情: 我尝试了睡眠,如果我加入了150毫秒的睡眠,那么响应是正确的。 但是这种方法会明显影响性能...... 由于offer方法返回true,我尝试了以下代码
boolean tempBool = false;
while(!tempBool){
tempBool = CommunicationThread.commandQueue.offer(temp);
System.out.println(tempBool);
}
打印出真实的。这没有影响。
我尝试在此while循环后打印temp[1]
,此时它是正确的值。(它打印出44
但消费者收到34
)
最有可能的情况是同步问题,但我认为基于BlockingQueue的对象的目的是解决这个问题。
非常感谢任何有关此BlockingQueue工作的帮助或建议。让我结束一个说明,这是我第一次在java中的线程之间使用队列,最后的程序将使用pi4j库在传感器上与传感器进行通信的Rasberry pi运行
答案 0 :(得分:0)
由于您询问了BlockingQueue的确切运作方式,请从以下开始:
阻塞队列是一个队列,当队列为空时,或者当队列已满时,当您尝试将项目排入队列时,阻塞队列会阻塞。尝试从空队列中出队的线程被阻塞,直到其他线程将一个项目插入队列。
这些阻塞队列阻止不同的线程读取/写入队列,因为它不可能,因为它是空的或满的。
正如 Andy Turner 和 JB Nizet 已经解释过的那样,变量在内存中是静态共享的。这意味着当你的线程读取队列时,它会找到一个引用(A.K.A.指针)到这个变量(在内存中)并在其中使用这个指针代码。然而,在它设法读取这些数据之前,你已经改变了变量,通常在非线程应用程序中,这不会成为一个问题,因为只有一个线程会尝试从内存中读取并且它将始终按时间顺序执行。避免这种情况的一种方法是每次向队列添加条目时使用变量数据创建一个新的变量/数组(它将自己分配给新的内存),这样就可以确保在之前不覆盖内存中的变量它由另一个线程处理。一个简单的方法是:
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);
希望这能解释这个主题,如果没有:随意提出其他问题:)