具有谓词的生产者 - 消费者

时间:2014-01-31 22:24:46

标签: java multithreading concurrency producer-consumer

我正在寻找支持在谓词上阻止read()的java集合。我写了一个简单的版本,但似乎这已经发明了吗?

例如:

interface PredicateConsumerCollection<T> {

  public void put(T t);

  @Nullable
  public T get(Predicate<T> p, long millis) throws InterruptedException;
}

put()将其参数传递给具有匹配谓词的等待使用者,或者将其存储在商店中。如果合适的get()已经存储在商店中,则T会立即返回,或者阻塞直到合适的值被放置()或超时。消费者竞争,但公平对我来说并不重要。

任何人都知道这样的收藏品吗?

2 个答案:

答案 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!"));
    }

}