我正在寻找支持在谓词上阻止read()
的java集合。我写了一个简单的版本,但似乎这已经发明了吗?
例如:
interface PredicateConsumerCollection<T> {
public void put(T t);
@Nullable
public T get(Predicate<T> p, long millis) throws InterruptedException;
}
put()
将其参数传递给具有匹配谓词的等待使用者,或者将其存储在商店中。如果合适的get()
已经存储在商店中,则T
会立即返回,或者阻塞直到合适的值被放置()或超时。消费者竞争,但公平对我来说并不重要。
任何人都知道这样的收藏品吗?
答案 0 :(得分:1)
没有直接的类可以解决您的问题,但ConcurrentHashMap和BlockingQueue的组合可能是一个解决方案。
哈希映射定义为:
final ConcurrentHashMap<Predicate, LinkedBlockingQueue<Result>> lookup;
put需要确保,对于每个Predicate队列都添加到地图中,可以使用putIfAbsent
完成线程安全。
如果您有一组固定的谓词,您只需预先填写列表,然后消费者只需拨打lookup.get(Predicate).take()
如果Predicates的数量未知/太多,您需要为消费者编写一个等待/通知实现,以防Predicate尚未出现在列表中。
答案 1 :(得分:0)
我还需要一些非常相似的东西来测试在某个超时内收到某个JMS异步消息。事实证明,通过使用Oracle tutorials中所述的基本等待/通知,您的问题相对容易实现。我们的想法是使put和query方法同步,让查询方法等待。 put方法调用notifyAll来唤醒查询方法中的任何等待线程。然后,查询方法必须检查谓词是否匹配。最棘手的事情是由于在谓词不匹配时唤醒并且由于可能的“虚假唤醒”而使得超时正确。我发现this stackoverflow post提供了答案。
以下是我提出的实施方案:
import java.util.ArrayList;
import java.util.List;
// import net.jcip.annotations.GuardedBy;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
public class PredicateConsumerCollectionImpl<T> implements
PredicateConsumerCollection<T> {
// @GuardedBy("this")
private List<T> elements = new ArrayList<>();
@Override
public synchronized void put(T t) {
elements.add(t);
notifyAll();
}
@Override
public synchronized T query(Predicate<T> p, long millis)
throws InterruptedException {
T match = null;
long nanosOfOneMilli = 1000000L;
long endTime = System.nanoTime() + millis * nanosOfOneMilli;
while ((match = Iterables.find(elements, p, null)) == null) {
long sleepTime = endTime - System.nanoTime();
if (sleepTime <= 0) {
return null;
}
wait(sleepTime / nanosOfOneMilli,
(int) (sleepTime % nanosOfOneMilli));
}
return match;
}
synchronized boolean contains(T t) {
return elements.contains(t);
}
}
这是一个JUnit测试,证明代码按预期工作:
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
import com.google.common.base.Predicate;
/**
* Unit test for the {@link PredicateConsumerCollection} implementation.
*
* <p>
* The tests act as consumers waiting for the test Producer to put a certain
* String.
*/
public class PredicateConsumerCollectionTest {
private static class Producer implements Runnable {
private PredicateConsumerCollection<String> collection;
public Producer(PredicateConsumerCollection<String> collection) {
this.collection = collection;
collection.put("Initial");
}
@Override
public void run() {
try {
int millis = 50;
collection.put("Hello");
Thread.sleep(millis);
collection.put("I");
Thread.sleep(millis);
collection.put("am");
Thread.sleep(millis);
collection.put("done");
Thread.sleep(millis);
collection.put("so");
Thread.sleep(millis);
collection.put("goodbye!");
} catch (InterruptedException e) {
e.printStackTrace();
fail("Unexpected InterruptedException");
}
}
}
private PredicateConsumerCollectionImpl<String> collection;
private Producer producer;
@Before
public void setup() {
collection = new PredicateConsumerCollectionImpl<>();
producer = new Producer(collection);
}
@Test(timeout = 2000)
public void wait_for_done() throws InterruptedException {
assertTrue(collection.contains("Initial"));
assertFalse(collection.contains("Hello"));
Thread producerThread = new Thread(producer);
producerThread.start();
String result = collection.query(new Predicate<String>() {
@Override
public boolean apply(String s) {
return "done".equals(s);
}
}, 1000);
assertEquals("done", result);
assertTrue(collection.contains("Hello"));
assertTrue(collection.contains("done"));
assertTrue(producerThread.isAlive());
assertFalse(collection.contains("goodbye!"));
producerThread.join();
assertTrue(collection.contains("goodbye!"));
}
@Test(timeout = 2000)
public void wait_for_done_immediately_happens() throws InterruptedException {
Thread producerThread = new Thread(producer);
producerThread.start();
String result = collection.query(new Predicate<String>() {
@Override
public boolean apply(String s) {
return "Initial".equals(s);
}
}, 1000);
assertEquals("Initial", result);
assertFalse(collection.contains("I"));
producerThread.join();
assertTrue(collection.contains("goodbye!"));
}
@Test(timeout = 2000)
public void wait_for_done_never_happens() throws InterruptedException {
Thread producerThread = new Thread(producer);
producerThread.start();
assertTrue(producerThread.isAlive());
String result = collection.query(new Predicate<String>() {
@Override
public boolean apply(String s) {
return "DONE".equals(s);
}
}, 1000);
assertEquals(null, result);
assertFalse(producerThread.isAlive());
assertTrue(collection.contains("goodbye!"));
}
}