Mit einem realen Gerät wird es dir schwer fallen, die besagten Fälle nachzustellen - das ist mit einem automatisierten Test schon schwierig genug.
Das Verhalten ist stark abhängig von der Implementierung. Ein Extrembeispiel ist die oben genannte SynchronousQueue, bei der put und take so lange blockieren, bis an beiden Enden der Queue ein Thread ist. put und take werden dann also nur gleichzeitig ausgeführt.
Eine unlimitierte Queue blockiert beim put normalerweise nicht. Daher wird ein offer darauf immer true zurückgeben.
Der Speicherbedarf ist ebenso abhängig von der Implementierung. Eine ArrayBlockingQueue sollte einen konstanten Speicherbedarf haben, weil die Positionen in der Queue im Konstruktor instantiiert werden. Bei der LinkedBlockingQueue wächst der Speicherbedarf mit der Anzahl eingereihter Elemente, weil sie dynamisch erzeugt werden. Dabei fällt dann aber Garbage an.
Ein kurzer Blick in die Implementierung ist äußerst aufschlussreich.
Ich habe mal eine Mini-Testsuite mit den drei Tests programmiert (mit der heißen Nadel gestrickt, ist also ggf. unsauber):
Tests
import java.util.concurrent.BlockingQueue;
public class IoCommand {
private final int adr; // Adresse des Gerätes
private final String cmdStr; // auszuführendes Kommando
private final BlockingQueue<String> answerQueue; // Queue für die Antwort
public IoCommand(int adr, String cmd, BlockingQueue<String> ans) {
this.adr = adr;
this.cmdStr = cmd;
this.answerQueue = ans;
}
public int getAdr() {
return adr;
}
public String getCmdStr() {
return cmdStr;
}
public BlockingQueue<String> getAnswerQueue() {
return answerQueue;
}
}```
```package queues;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
public class CommandExecutor {
private static final long TIMEOUT = 100L;
private final BlockingQueue<IoCommand> commandQueue;
public CommandExecutor(BlockingQueue<IoCommand> commandQueue) {
this.commandQueue = commandQueue;
}
public String executeWithTimeout(int adr, String cmd) {
BlockingQueue<String> answerQueue = new ArrayBlockingQueue<>(1);
try {
if (commandQueue.offer(new IoCommand(adr, cmd, answerQueue), TIMEOUT, TimeUnit.MILLISECONDS))
return answerQueue.poll(TIMEOUT, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return null;
}
}
import org.testng.Assert;
import org.testng.annotations.Test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@Test
public class CommandExecutorTest {
public void returnsAnswerIfTimingIsCorrect() {
final BlockingQueue<IoCommand> commandQueue = new ArrayBlockingQueue<>(1);
CommandExecutor commandExecutor = new CommandExecutor(commandQueue);
int anyNumber = 3;
String anyString = "dummy";
new Thread(new Runnable() {
@Override
public void run() {
try {
final IoCommand command = commandQueue.take();
command.getAnswerQueue().put("response");
} catch (InterruptedException ignored) {
}
}
}).start();
String result = commandExecutor.executeWithTimeout(anyNumber, anyString);
Assert.assertEquals(result, "response");
}
public void returnsNullIfWorkerIsTooSlow() {
final BlockingQueue<IoCommand> commandQueue = new ArrayBlockingQueue<>(1);
CommandExecutor commandExecutor = new CommandExecutor(commandQueue);
int anyNumber = 3;
String anyString = "dummy";
new Thread(new Runnable() {
@Override
public void run() {
try {
final IoCommand command = commandQueue.take();
Thread.sleep(120);
command.getAnswerQueue().put("response");
} catch (InterruptedException ignored) {
}
}
}).start();
String result = commandExecutor.executeWithTimeout(anyNumber, anyString);
Assert.assertNull(result);
}
public void returnsNullIfCommandQueueIsFull() throws InterruptedException {
final BlockingQueue<IoCommand> commandQueue = new ArrayBlockingQueue<>(1);
CommandExecutor commandExecutor = new CommandExecutor(commandQueue);
int anyNumber = 3;
String anyString = "dummy";
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(120);
commandQueue.take();
final IoCommand command = commandQueue.take();
command.getAnswerQueue().put("response");
} catch (InterruptedException ignored) {
}
}
}).start();
commandQueue.put(new IoCommand(anyNumber, null, null));
String result = commandExecutor.executeWithTimeout(anyNumber, anyString);
Assert.assertNull(result);
}
}```
*** Edit ***
Btw: ich würde mich freuen, wenn jemand mit TDD Erfahrung sich die Tests mal ansehen könnte und kurz dazu sagt, ob das so in Ordnung wäre.