synchronize and merge messaging/data flow - java

It is about very common sensor data processing problem.
To synchronize and merge sensor data from different sources, I would like to implement it in Java without too complicated 3rd libs or framework.
Say, I define an object (O) which consists of, for example, 4 attributes (A1,..A4). The 4 attributes come from different data channels, e.g. socket channel.
The 4 attributes arrive generally in a rate of 1.0 ~ 2.0 Hz and their arrivals are independent from each other.
Once there are 4 attributes (A1, ..A4) coming at the same time (within a small time window, e.g. 100ms), then I construct a new object (O) from those 4 attributes.
a descriptive scenario is as follows.
the arrival time point of A1 ~ A4 is marked with *.
Objects O1 ~ U3 are constructed on the time point of t1, t2 and t3 respectively.
Some attributes arrives between t2 and t3, but are not complete for constructing an Object, therefore they
would be dropped and ignored.
A1 * * * *
A2 * * * *
A3 * * *
A4 * * * *
--------|------------|-----------------|----------> time
t1 t2 t3
O1 O2 O3
some requirements:
identify the time point a.s.a.p. to construct a object from the last incoming 4 attributes.
FIFO, O1 must be constructed before O2, and so on.
less locking in Java
drop data eventually if they are not complete to construct a object.
Some quick idea on implementation are:
store any incoming attributes in a FIFO queue of time-discrete buckets (each bucket contains 4 different attributes).
run an endless thread concurrently to check the FIFO queue (from the head of the queue) if any bucket is already filled with 4 different attributes. If yes, then construct an object and remove the bucket from the queue. If a bucket is not complete filled within a specific time window, it will be dropped.
any suggestion and correction is welcome!

This is unlikely to solve your problem, but it might point you in the right direction.
I would use Google Guava's MapMaker for a first attempt:
ConcurrentMap<Key, Bucket> graphs = new MapMaker()
.expireAfterAccess(100, TimeUnit.MILLISECOND)
.makeComputingMap(new Function<Key, Bucket>() {
public Bucket apply(Key key) {
return new Bucket(key);
}
});
This would create a map whose entries would disappear if they had not been accessed for 100 ms, and creates a new bucket when it is asked for.
What I can't work out is exactly what the Key would be :S What you're really after is the same kind of functionality in the form of a queue.

Here's another crazy idea:
use one single LinkedBlockingQueue to write values to from all sensors A1-A4
assign this queue to AtomicReference variable
create a timer task which will switch this queue with a new one at specified intervals (100ms)
fetch all data from the old queue and see if you have all data A1-A4
if yes, then create the object, otherwise drop everything

This is another way of doing it - it's just pseudocode though, you'll need to write it yourself :)
class SlidingWindow {
AtomicReference<Object> a1;
AtomicReference<Object> a2;
AtomicReference<Object> a3;
AtomicReference<Object> a4;
Queue<Long> arrivalTimes = new Queue(4);
public Bucket setA1(Object data) {
a1.set(data);
now = System.currentTimeInMillis()
long oldestArrivalTime = arrivalTimes.pop();
arrivalTimes.push(now);
if (now - oldestArrivalTime < 100) {
return buildBucket();
}
return null;
}
public Bucket setA2(Object data) { ...
...
private Bucket buildBucket() {
Bucket b = new Bucket(a1, a2, a3, a4);
a1.clear();
a2.clear();
a3.clear();
a4.clear();
return b;
}
}

You could do something like this, the get operation is blocking till data has arrived, the add operation is not blocking. The get operation could be optimized a bit so that you keep candidates in a paralell structure so that you don't need to iterate over all candidates when filtering out old items. Iterating over 4 items should however be fast enough.
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.LinkedBlockingQueue;
public class Filter<V> {
private static final long MAX_AGE_IN_MS = 100;
private final int numberOfSources;
private final LinkedBlockingQueue<Item> values = new LinkedBlockingQueue<Item>();
public Filter(int numberOfSources) {
this.numberOfSources = numberOfSources;
}
public void add(String source, V data) {
values.add(new Item(source, data));
}
public void get() throws InterruptedException {
HashMap<String, Item> result = new HashMap<String, Item>();
while (true) {
while (result.size() < numberOfSources) {
Item i = values.take();
result.put(i.source, i);
if (result.size() == numberOfSources) {
break;
}
}
//We got candidates from each source now, check if some are too old.
long now = System.currentTimeMillis();
Iterator<Item> it = result.values().iterator();
while (it.hasNext()) {
Item item = it.next();
if (now - item.creationTime > MAX_AGE_IN_MS) {
it.remove();
}
}
if (result.size() == numberOfSources) {
System.out.println("Got result, create a result object and return the items " + result.values());
break;
}
}
}
private class Item {
final String source;
final V value;
final long creationTime;
public Item(String source, V value) {
this.source = source;
this.value = value;
this.creationTime = System.currentTimeMillis();
}
public String toString() {
return String.valueOf(value);
}
}
public static void main(String[] args) throws Exception {
final Filter<String> filter = new Filter<String>(4);
new Thread(new Runnable() {
public void run() {
try {
filter.get();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
filter.add("a0", "va0.1");
filter.add("a0", "va0.2");
Thread.sleep(2000);
filter.add("a0", "va0.3");
Thread.sleep(100);
filter.add("a1", "va1.1");
filter.add("a2", "va2.1");
filter.add("a0", "va0.4");
Thread.sleep(100);
filter.add("a3", "va3.1");
Thread.sleep(10);
filter.add("a1", "va1.2");
filter.add("a2", "va2.2");
filter.add("a0", "va0.5");
}
}

Related

Using Chronicle Map producing garbage while using Streams API

Today I was experimenting with Chronicle Map. Here is a code sample:
package experimental;
import net.openhft.chronicle.core.values.IntValue;
import net.openhft.chronicle.map.ChronicleMap;
import net.openhft.chronicle.values.Values;
public class Tmp {
public static void main(String[] args) {
try (ChronicleMap<IntValue, User> users = ChronicleMap
.of(IntValue.class, User.class)
.name("users")
.entries(100_000_000)
.create();) {
User user = Values.newHeapInstance(User.class);
IntValue id = Values.newHeapInstance(IntValue.class);
for (int i = 1; i < 100_000_000; i++) {
user.setId(i);
user.setBalance(Math.random() * 1_000_000);
id.setValue(i);
users.put(id, user);
if (i % 100 == 0) {
System.out.println(i + ". " +
users.values()
.stream()
.max(User::compareTo)
.map(User::getBalance)
.get());
}
}
}
}
public interface User extends Comparable<User> {
int getId();
void setId(int id);
double getBalance();
void setBalance(double balance);
#Override
default int compareTo(User other) {
return Double.compare(getBalance(), other.getBalance());
}
}
}
As you see in above code I am just creating User object and putting it in Chronicle Map, and after each 100th record I am just printing the User with max balance. But unfortunately it is producing some garbage. When I monitored it with VisualVM I got the following:
It seems using streams in Chronicle Map will produce garbage anyway.
So my questions are:
* Does this mean that I should not use Streams API with Chronicle Map.
* Are there any other solutions/ways of doing this?
* How to filter/search Chronicle Map in proper way because I have use cases other than
just putting/getting data in it.
ChronicleMap's entrySet().iterator() (as well as iterator on keySet() and values()) is implemented so that it dumps all objects in a Chronicle Map's segment into memory before iterating over them.
You can inspect how much segments do you have by calling map.segments(). You could also configure it during the ChronicleMap construction phase, check out ChronicleMapBuilder javadoc.
So, during iteration, you should expect regularly, approximately numEntries / numSegments entries to be dumped into memory at once, where numEntries is the size of your Chronicle Map.
You can implement streaming processing on a Chronicle Map avoiding creating a lot of garbage, by reusing objects, via Segment Context API:
User[] maxUser = new User[1];
for (int i = 0; i < users.segments(); i++) {
try (MapSegmentContext<IntValue, User, ?> c = map.segmentContext(i)) {
c.forEachSegmentEntry((MapEntry<IntValue, User> e) -> {
User user = e.value().get();
if (maxUser[0] == null || user.compareTo(maxUser[0]) > 0) {
// Note that you cannot just assign `maxUser[0] = user`:
// this object will be reused by the SegmentContext later
// in the iteration, and it's contents will be rewritten.
// Check out the doc for Data.get().
if (maxUser[0] == null) {
maxUser[0] = Values.newHeapInstance(User.class);
}
User newMaxUser = e.value().getUsing(maxUser[0]);
// assert the object is indeed reused
assert newMaxUser == maxUser[0];
}
});
}
}
Link to doc for Data.get().
The code of the above example is adapted from here.

Split Java stream into two lazy streams without terminal operation

I understand that in general Java streams do not split. However, we have an involved and lengthy pipeline, at the end of which we have two different types of processing that share the first part of the pipeline.
Due to the size of the data, storing the intermediate stream product is not a viable solution. Neither is running the pipeline twice.
Basically, what we are looking for is a solution that is an operation on a stream that yields two (or more) streams that are lazily filled and able to be consumed in parallel. By that, I mean that if stream A is split into streams B and C, when streams B and C consume 10 elements, stream A consumes and provides those 10 elements, but if stream B then tries to consume more elements, it blocks until stream C also consumes them.
Is there any pre-made solution for this problem or any library we can look at? If not, where would we start to look if we want to implement this ourselves? Or is there a compelling reason not to implemented at all?
I don't know about functionality that would fulfill your blocking requirement, but you might be interested in jOOλ's Seq.duplicate() method:
Stream<T> streamA = Stream.of(/* your data here */);
Tuple2<Seq<T>, Seq<T>> streamTuple = Seq.seq(streamA).duplicate();
Stream<T> streamB = streamTuple.v1();
Stream<T> streamC = streamTuple.v2();
The Streams can be consumed absolutely independently (including consumption in parallel) thanks to the SeqBuffer class that's used internally by this method.
Note that:
SeqBuffer will cache even the elements that are no longer needed because they have already been consumed by both streamB and streamC (so if you cannot afford to keep them in memory, it's not a solution for you);
as I mentioned at the beginning, streamB and streamC will not block one another.
Disclaimer: I am the author of the SeqBuffer class.
You can implement a custom Spliterator in order to achieve such behavior. We will split your streams into the common "source" and the different "consumers". The custom spliterator then forwards the elements from the source to each consumer. For this purpose, we will use a BlockingQueue (see this question).
Note that the difficult part here is not the spliterator/stream, but the syncing of the consumers around the queue, as the comments on your question already indicate. Still, however you implement the syncing, Spliterator helps to use streams with it.
#SafeVarargs
public static <T> long streamForked(Stream<T> source, Consumer<Stream<T>>... consumers)
{
return StreamSupport.stream(new ForkingSpliterator<>(source, consumers), false).count();
}
private static class ForkingSpliterator<T>
extends AbstractSpliterator<T>
{
private Spliterator<T> sourceSpliterator;
private BlockingQueue<T> queue = new LinkedBlockingQueue<>();
private AtomicInteger nextToTake = new AtomicInteger(0);
private AtomicInteger processed = new AtomicInteger(0);
private boolean sourceDone;
private int consumerCount;
#SafeVarargs
private ForkingSpliterator(Stream<T> source, Consumer<Stream<T>>... consumers)
{
super(Long.MAX_VALUE, 0);
sourceSpliterator = source.spliterator();
consumerCount = consumers.length;
for (int i = 0; i < consumers.length; i++)
{
int index = i;
Consumer<Stream<T>> consumer = consumers[i];
new Thread(new Runnable()
{
#Override
public void run()
{
consumer.accept(StreamSupport.stream(new ForkedConsumer(index), false));
}
}).start();
}
}
#Override
public boolean tryAdvance(Consumer<? super T> action)
{
sourceDone = !sourceSpliterator.tryAdvance(queue::offer);
return !sourceDone;
}
private class ForkedConsumer
extends AbstractSpliterator<T>
{
private int index;
private ForkedConsumer(int index)
{
super(Long.MAX_VALUE, 0);
this.index = index;
}
#Override
public boolean tryAdvance(Consumer<? super T> action)
{
// take next element when it's our turn
while (!nextToTake.compareAndSet(index, index + 1))
{
}
T element;
while ((element = queue.peek()) == null)
{
if (sourceDone)
{
// element is null, and there won't be no more, so "terminate" this sub stream
return false;
}
}
// push to consumer pipeline
action.accept(element);
if (consumerCount == processed.incrementAndGet())
{
// start next round
queue.poll();
processed.set(0);
nextToTake.set(0);
}
return true;
}
}
}
With the approach used, the consumers work on each element in parallel, but wait for each other before starting on the next element.
Known issue
If one of the consumers is "shorter" than the others (e.g. because it calls limit()) it will also stop the other consumers and leave the threads hanging.
Example
public static void sleep(long millis)
{
try { Thread.sleep((long) (Math.random() * 30 + millis)); } catch (InterruptedException e) { }
}
streamForked(Stream.of("1", "2", "3", "4", "5"),
source -> source.map(word -> { sleep(50); return "fast " + word; }).forEach(System.out::println),
source -> source.map(word -> { sleep(300); return "slow " + word; }).forEach(System.out::println),
source -> source.map(word -> { sleep(50); return "2fast " + word; }).forEach(System.out::println));
fast 1
2fast 1
slow 1
fast 2
2fast 2
slow 2
2fast 3
fast 3
slow 3
fast 4
2fast 4
slow 4
2fast 5
fast 5
slow 5

Java cross thread / cross object Map sharing + generics

Good evening Java wizards,
I am fairly new to Java, and would like to be educated on the mistake I am not able to resolve after some hours of research.
Assume a program that has the main thread with some variables that could dynamically read/lookup value in a Map of values. This Map is shared by threads and objects accross the application.
There would be one or more separate threads updating the Map with values thus the map will be synchronized - I am considering ConcurrentHashMap.
When the reading thread arrives to one of the variables with the dynamic values it will reach in to the cross-thread shared Map and retreive its latest value against its key.
I have some prototype code here - it compliles and seam to be running as expected. However it is set to work only with a String variable and values.
Here is the code:
<code>
/*
* Cross thread / cross object shared Map
* some threads update the map
* other threads read the map for most current values (updated by other threads)
* associated with the provided key
*/
public class CrossThreadUpdatableValues {
private static final Map<Integer, String> list = new ConcurrentHashMap<>();
// return the last updated value
// from the map
public static String getValue(Integer key) {
return list.get(key);
}
// update/write the passed in value in to the map
// (in to the element having the given key)
public static void setValue(Integer key, String ev) {
list.put(key, ev);
}
}
/*
* Thread 1
* 10 loops that update the cross thread / cross object shared Map
* with unique values
*/
public class Thread1 extends Thread {
private final int delay;
private final int threadNum = 1;
Thread1(int delay) {
this.delay = delay;
}
public void run(){
for (int i=0;i<10;i++) {
String v1 = threadNum + "-AAA"; Integer key1 = 1;
String v2 = threadNum + "-BBB"; Integer key2 = 2;
String v3 = threadNum + "-CCC"; Integer key3 = 3;
CrossThreadUpdatableValues.setValue(key1, v1);
CrossThreadUpdatableValues.setValue(key2, v2);
CrossThreadUpdatableValues.setValue(key3, v3);
System.out.println("Map values updated by Thread " + threadNum + ", loop " + i);
try {
Thread.sleep(this.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Thread2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
/*
* Thread 2 (similar to Thread 1)
* 10 loops that update the cross thread / cross object shared Map
* with unique values
*/
public class Thread2 extends Thread {
private final int delay;
private final int threadNum = 2;
Thread2(int delay) {
this.delay = delay;
}
public void run(){
for (int i=0;i<10;i++) {
String v1 = threadNum + "-XXX"; Integer key1 = 1;
String v2 = threadNum + "-YYY"; Integer key2 = 2;
String v3 = threadNum + "-ZZZ"; Integer key3 = 3;
CrossThreadUpdatableValues.setValue(key1, v1);
CrossThreadUpdatableValues.setValue(key2, v2);
CrossThreadUpdatableValues.setValue(key3, v3);
System.out.println("Map values updated by Thread " + threadNum + ", loop " + i);
try {
Thread.sleep(this.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Thread2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
/*
* Reading thread -
* 20 loops that read the cross thread / crooss object shared Map
* for the most current values updated by other threads in various intervals
*/
public class ThreadRead extends Thread {
private final int delay;
private final int threadNum = 0;
ThreadRead(int delay) {
this.delay = delay;
}
public void run(){
Integer key1 = 1;
Integer key2 = 2;
Integer key3 = 3;
for (int i=0;i<20;i++) {
String v1 = CrossThreadUpdatableValues.getValue(key1);
String v2 = CrossThreadUpdatableValues.getValue(key1);
String v3 = CrossThreadUpdatableValues.getValue(key1);
System.out.println(" - - - Map values read by (reading) thread " + threadNum + ", loop " + i + "; v1 = " + v2 + "; v1 = " + v2 + "; v3 = " + v3);
try {
Thread.sleep(this.delay);
} catch (InterruptedException ex) {
Logger.getLogger(Thread2.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
/**
*
* Main test class - start of test run
*/
public class Test_Main {
public static void main(String[] args) {
// start thread that populates the shared Map with unique values
// in 5 second intervals (10x)
Thread1 thread1 = new Thread1(5000);
thread1.start();
// start thread that populates the shared Map with unique values
// in 10 second intervals (10x)
Thread2 thread2 = new Thread2(10000);
thread2.start();
// start thread that reads the shared Map of unique values
// - the latest updates from any previous thread
// in 5 second intervals (20x)
ThreadRead threadRead = new ThreadRead(5000);
threadRead.start();
}
}
</code>
These dynamic variables will naturally be of different types (Integers, Strings, etc.), so I am considering using generics, BUT that gives me the uneducated headache. As the Map needs to be shared between all involved classes and threads, it needs to be declared static and Java won't permit the use of generics on this static Map.
Here is the modification of above class CrossThreadUpdatableValues using generics - that will NOT work but a hint what I am trying to achieve:
<code>
/*
* Cross thread / crooss object shared Map
* some threads update the map
* other threads read the map for most current values (udated by other threads)
* associated with the provided key
*/
public class CrossThreadUpdatableValues<K, V> {
private static final Map<K, V> list = new ConcurrentHashMap<>();
// return the last updated value
// from the map
public static V getValue(K key) {
return list.get(key);
}
// update/write the passed in value in to the map
// (in to the element having the given key)
public static void setValue(K key, V v) {
list.put(key, v);
}
}
</code>
I would appreciate your input into how to approach this in a thread save manner, allow handling various types of variables (I know Object could be used instead of V but is it the right way to go?) and perhaps point out some hints or references on a solution or a better approach.
Thank you
If you want to save different types in the Map you will need to use Object, using generics will force a design where a specific map is created for each combination of <K, V>.
If you want to store in the same map differents kind of objects, you don´t need generics, declare your map as
Map <String, Object>
Otherwise, you can use an interface like this
package a;
public interface ICrossThreadUpdatableValues<K, V> {
// return the last updated value
// from the map
V getValue(K key);
// update/write the passed in value in to the map
// (in to the element having the given key)
void setValue(K key, V v);
}
And then do a concrete implementation for the desired types like this
package a;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/*
* Cross thread / crooss object shared Map
* some threads update the map
* other threads read the map for most current values (udated by other threads)
* associated with the provided key
*/
public class IntegerCrossThreadUpdatableValues implements ICrossThreadUpdatableValues<String, Integer> {
private final Map<String, Integer> list = new ConcurrentHashMap<>();
private static IntegerCrossThreadUpdatableValues instance;
private IntegerCrossThreadUpdatableValues() {
}
// return the last updated value
// from the map
public Integer getValue(String key) {
return list.get(key);
}
// update/write the passed in value in to the map
// (in to the element having the given key)
public void setValue(String key, Integer v) {
list.put(key, v);
}
public static IntegerCrossThreadUpdatableValues getInstance() {
if (instance == null) {
instance = new IntegerCrossThreadUpdatableValues();
}
return instance;
}
}
Note that the implementation defines a singleton pattern, you can´t instantiate the class outside of it and you can get always the same object calling 'getInstance'

Java look at elements in queue

So I'm making a search algorithm. I'm using a queue to store all of my objects
This is how I initialised it
Queue<Node> queue = new LinkedList<Node>();
I want to compare a variable in each object and order to queue. My plan is to use a for loop to compare the first object with each of the other objects and whichever object has the lowest variable is sent to the front of the queue. Then move onto the next object and repeat the process. My issue is I'm not sure how to retrieve an object from the queue that isn't the first object in the queue....
You could do a for loop through the Queue:
for (Node n : queue) {
do stuff with n
}
However, you aren't going to be able to remove items from the middle of the queue. Might I suggest a structure like an ArrayList?
In my opinion the best way is to use PriorityQueue. You can specify implementation of Comparator interface that will impose how elements should be sorted inside of queue.
Here is an example:
Let's say that this is your Node class:
public class Node {
// this field will be used to sort in queue
private int value;
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
#Override
public String toString() {
return "My value is: " + value;
}
}
And here is example of adding Nodes into queue:
import java.util.PriorityQueue;
import java.util.Random;
public class QueueExample {
public static void main(String[] args) {
Random r = new Random();
// Priority queue with custom comparator
PriorityQueue<Node> queue = new PriorityQueue<Node>(10, new SampleNodeComparator());
// adding 100 nodes with random value
for(int i = 0; i < 100; ++i) {
queue.add( new Node(r.nextInt(1000)));
}
// nodes will be removed from queue in order given by comparator
while(queue.size() != 0) {
System.out.println(queue.remove());
}
}
}
And the most important part - implementation of our custom comparator
import java.util.Comparator;
// our comparator needs to implements Comparator interface
public class SampleNodeComparator implements Comparator<Node> {
#Override
public int compare(Node o1, Node o2) {
/*
value that should be return from compare method should follow rules:
if o1 == o2 - return 0
if o1 > o2 - return any positive value
if o1 < 02 - return any negative value
*/
return o1.getValue() - o2.getValue();
}
}
When you run main method from QueueExample class you will see on console that values are removed from queue sorted by Node.value value.
Use Queue<E>#peek () to retrieve an object without removing it.
Some example code:
import java.util.*;
class Example {
public static void main (String[] args) throws Exception {
Queue<String> list = new PriorityQueue<>();
{ // Initialize the Queue
list.add ("Hello ");
list.add ("Mrs. ");
list.add ("DoubtFire! ");
}
System.out.println (list);
// Iterating through the Queue
String element;
while ( (element = list.peek()) != null) {
if (element.equals ("Mrs. ")) {
System.out.println ("\"Mrs\" found!");
}
System.out.println (element);
list.remove (element);
}
System.out.println (list); // Empty by now...
}
}
Output:
[DoubtFire! , Mrs. , Hello ]
DoubtFire!
Hello
"Mrs" found!
Mrs.
[]
Queue interface does not guarantee any particular order while iterating or polling so theoretically this task is impossible to implement with Queue.
Seeing your response to my comment, I think that in your case, you should use the PriorityQueue because it does what you need without needing you to reinvent the wheel, which is usually not recommended.
By default, the priority queue will use the default implementation of the compareTo method. Assuming that you have a composite type, you have two options:
You can make your custom class implement the Comparabale interface and have your sorting logic there.
Alternatively, you could pass your own comparator:
PriorityQueue<..> p = new PriorityQueue<..>(5, new Comparator<..>()
{
#override
public int compare(.. type1, .. type2)
{
//comparison logic done here.
}
}
You can take a look at this short tutorial for more information.

Advice for efficient blocking queries

I would like to store tuples objects in a concurent java collection and then have an efficient, blocking query method that returns the first element matching a pattern. If no such element is available, it would block until such element is present.
For instance if I have a class:
public class Pair {
public final String first;
public final String Second;
public Pair( String first, String second ) {
this.first = first;
this.second = second;
}
}
And a collection like:
public class FunkyCollection {
public void add( Pair p ) { /* ... */ }
public Pair get( Pair p ) { /* ... */ }
}
I would like to query it like:
myFunkyCollection.get( new Pair( null, "foo" ) );
which returns the first available pair with the second field equalling "foo" or blocks until such element is added. Another query example:
myFunkyCollection.get( new Pair( null, null ) );
should return the first available pair whatever its values.
Does a solution already exists ? If it is not the case, what do you suggest to implement the get( Pair p ) method ?
Clarification: The method get( Pair p) must also remove the element. The name choice was not very smart. A better name would be take( ... ).
Here's some source code. It basically the same as what cb160 said, but having the source code might help to clear up any questions you may still have. In particular the methods on the FunkyCollection must be synchronized.
As meriton pointed out, the get method performs an O(n) scan for every blocked get every time a new object is added. It also performs an O(n) operation to remove objects. This could be improved by using a data structure similar to a linked list where you can keep an iterator to the last item checked. I haven't provided source code for this optimization, but it shouldn't be too difficult to implement if you need the extra performance.
import java.util.*;
public class BlockingQueries
{
public class Pair
{
public final String first;
public final String second;
public Pair(String first, String second)
{
this.first = first;
this.second = second;
}
}
public class FunkyCollection
{
final ArrayList<Pair> pairs = new ArrayList<Pair>();
public synchronized void add( Pair p )
{
pairs.add(p);
notifyAll();
}
public synchronized Pair get( Pair p ) throws InterruptedException
{
while (true)
{
for (Iterator<Pair> i = pairs.iterator(); i.hasNext(); )
{
Pair pair = i.next();
boolean firstOk = p.first == null || p.first.equals(pair.first);
boolean secondOk = p.second == null || p.second.equals(pair.second);
if (firstOk && secondOk)
{
i.remove();
return pair;
}
}
wait();
}
}
}
class Producer implements Runnable
{
private FunkyCollection funkyCollection;
public Producer(FunkyCollection funkyCollection)
{
this.funkyCollection = funkyCollection;
}
public void run()
{
try
{
for (int i = 0; i < 10; ++i)
{
System.out.println("Adding item " + i);
funkyCollection.add(new Pair("foo" + i, "bar" + i));
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
}
public void go() throws InterruptedException
{
FunkyCollection funkyCollection = new FunkyCollection();
new Thread(new Producer(funkyCollection)).start();
System.out.println("Fetching bar5.");
funkyCollection.get(new Pair(null, "bar5"));
System.out.println("Fetching foo2.");
funkyCollection.get(new Pair("foo2", null));
System.out.println("Fetching foo8, bar8");
funkyCollection.get(new Pair("foo8", "bar8"));
System.out.println("Finished.");
}
public static void main(String[] args) throws InterruptedException
{
new BlockingQueries().go();
}
}
Output:
Fetching bar5.
Adding item 0
Adding item 1
Adding item 2
Adding item 3
Adding item 4
Adding item 5
Fetching foo2.
Fetching foo8, bar8
Adding item 6
Adding item 7
Adding item 8
Finished.
Adding item 9
Note that I put everything into one source file to make it easier to run.
I know of no existing container that will provide this behavior. One problem you face is the case where no existing entry matches the query. In that case, you'll have to wait for new entries to arrive, and those new entries are supposed to arrive at the tail of the sequence. Given that you're blocking, you don't want to have to examine all the entries that precede the latest addition, because you've already inspected them and determined that they don't match. Hence, you need some way to record your current position, and be able to search forward from there whenever a new entry arrives.
This waiting is a job for a Condition. As suggested in cb160's answer, you should allocate a Condition instance inside your collection, and block on it via Condition#await(). You should also expose a companion overload to your get() method to allow timed waiting:
public Pair get(Pair p) throws InterruptedException;
public Pair get(Pair p, long time, TimeUnit unit) throws InterruptedException;
Upon each call to add(), call on Condition#signalAll() to unblock the threads waiting on unsatisfied get() queries, allowing them to scan the recent additions.
You haven't mentioned how or if items are ever removed from this container. If the container only grows, that simplifies how threads can scan its contents without worrying about contention from other threads mutating the container. Each thread can begin its query with confidence as to the minimum number of entries available to inspect. However, if you allow removal of items, there are many more challenges to confront.
In your FunkyCollection add method you could call notifyAll on the collection itself every time you add an element.
In the get method, if the underlying container (Any suitable conatiner is fine) doesn't contain the value you need, wait on the FunkyCollection. When the wait is notified, check to see if the underlying container contains the result you need. If it does, return the value, otherwise, wait again.
It appears you are looking for an implementation of Tuple Spaces. The Wikipedia article about them lists a few implementations for Java, perhaps you can use one of those. Failing that, you might find an open source implementation to imitate, or relevant research papers.

Categories

Resources