package jadex.commons.collection;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.LinkedList;
import java.util.List;
/**
* A blocking queue allows to enqueue or dequeue
* elements. It blocks, when it is tried to dequeue
* an element, but the queue is empty.
*/
public class BlockingQueue implements IBlockingQueue
{
//-------- attributes --------
/** The element storage. */
protected List elems;
/** The queue state. */
protected boolean closed;
/** The monitor. */
protected Object monitor;
//-------- constructors --------
/**
* Create a blocking queue.
*/
public BlockingQueue()
{
this.elems = new LinkedList();
this.monitor = new Object();
}
//-------- methods --------
/**
* Enqueue an element.
* @param element The element.
*/
public void enqueue(Object element)
{
if(closed)
throw new IBlockingQueue.ClosedException("Queue closed.");
synchronized(monitor)
{
this.elems.add(element);
monitor.notify();
}
}
/**
* Dequeue an element.
* @param timeout the time to wait (in millis) or -1 for no timeout.
* @return The element. When queue is empty
* the methods blocks until an element is added or the timeout occurs.
*/
public Object dequeue(long timeout) throws ClosedException, TimeoutException
{
if(closed)
throw new IBlockingQueue.ClosedException("Queue closed.");
synchronized(monitor)
{
if(timeout!=-1)
{
if(elems.isEmpty())
{
try
{
monitor.wait(timeout);
}
catch(InterruptedException e){}
if(closed)
throw new IBlockingQueue.ClosedException("Queue closed.");
if(elems.isEmpty())
throw new IBlockingQueue.TimeoutException("Timeout during dequeue().");
}
}
else
{
// Uses a spin lock, because wait must not be implemented atomically.
while(elems.isEmpty())
{
try
{
monitor.wait();
}
catch(InterruptedException e){}
if(closed)
throw new IBlockingQueue.ClosedException("Queue closed.");
}
}
return elems.remove(0);
}
}
/**
* Dequeue an element.
* @return The element. When queue is empty
* the methods blocks until an element is added.
*/
public Object dequeue() throws ClosedException
{
return dequeue(-1);
}
/**
* Open/close the queue.
* @param closed The closed state.
*/
public void setClosed(boolean closed)
{
if(!this.closed)
{
synchronized(monitor)
{
this.closed = closed;
monitor.notifyAll();
}
}
}
/**
* Return the size of the queue.
*/
public int size()
{
synchronized(monitor)
{
return this.elems.size();
}
}
//-------- static part --------
/**
* Main for testing.
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException
{
test1(args);
Thread.sleep(2000);
test1b(args);
Thread.sleep(2000);
test2(args);
}
/**
* Main for testing.
* @throws InterruptedException
*/
public static void test1(String[] args) throws InterruptedException
{
final int max = 5000000;
final int[] stat = new int[3];
final IBlockingQueue queue = new BlockingQueue();
// final java.util.concurrent.BlockingQueue queue = new LinkedBlockingQueue();
new Thread(new Runnable()
{
public void run()
{
while(true)
{
try
{
/*Object item =*/ queue.dequeue();
// Object item = queue.take();
// System.out.println("Processing: "+item);
stat[1] = Math.max(queue.size(), stat[1]);
if(queue.size()==0)
{
stat[0]++;
if(queue.size()==0 && stat[2]!=0)
{
System.out.println("Queue is now empty.");
break;
}
}
}
catch(Exception e)
{
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
throw new RuntimeException(sw.toString());
}
}
}
}).start();
for(int i=0; i<max; i++)
{
// System.out.println("Enqueing: Item "+i);
queue.enqueue("Item");
// queue.put("Item");
stat[1] = Math.max(queue.size(), stat[1]);
}
stat[2] = 1; // Flag to indicate end of test.
System.out.println("Queue was empty "+stat[0]+" times.\n"
+"Queue size is now "+queue.size()+".\n"
+"Max queue size was "+stat[1]+".");
}
/**
* Main for testing.
* @throws InterruptedException
*/
public static void test1b(String[] args) throws InterruptedException
{
final int max = 5000000;
final int[] stat = new int[3];
final ArrayBlockingQueue queue = new ArrayBlockingQueue();
// final java.util.concurrent.BlockingQueue queue = new LinkedBlockingQueue();
new Thread(new Runnable()
{
public void run()
{
while(true)
{
try
{
Object item = queue.dequeue();
// Object item = queue.take();
System.out.println("Processing: "+item);
stat[1] = Math.max(queue.size(), stat[1]);
if(queue.size()==0)
{
stat[0]++;
if(queue.size()==0 && stat[2]!=0)
{
System.out.println("Queue is now empty.");
break;
}
}
}
catch(Exception e)
{
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
throw new RuntimeException(sw.toString());
}
}
}
}).start();
for(int i=0; i<max; i++)
{
// System.out.println("Enqueing: Item "+i);
queue.enqueue("Item");
// queue.put("Item");
stat[1] = Math.max(queue.size(), stat[1]);
}
stat[2] = 1; // Flag to indicate end of test.
System.out.println("Queue was empty "+stat[0]+" times.\n"
+"Queue size is now "+queue.size()+".\n"
+"Max queue size was "+stat[1]+".");
}
/**
* Main for testing.
* @throws InterruptedException
*/
public static void test2(String[] args) throws InterruptedException
{
final Object monitor = new Object();
final int num = 5000000;
final int[] max = new int[1];
final int[] counter = new int[1];
final boolean[] finished = new boolean[1];
new Thread(new Runnable()
{
public void run()
{
int wait = 0;
System.out.println("Thread started.");
while(counter[0]>0 || !finished[0])
{
synchronized(monitor)
{
try
{
if(counter[0]==0)
{
monitor.wait();
wait++;
}
if(counter[0]==0)
throw new RuntimeException();
counter[0]--;
}
catch(InterruptedException e)
{
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
throw new RuntimeException(sw.toString());
}
}
}
System.out.println("Thread finished. Waits: "+wait);
}
}).start();
System.out.println("Main started.");
for(int i=0; i<num; i++)
{
synchronized(monitor)
{
counter[0]++;
max[0] = Math.max(counter[0], max[0]);
monitor.notify();
// Thread.yield();
}
}
finished[0] = true;
System.out.println("Main finished. Max is "+max[0]);
}
}