//package org.j4me.collections;
/**
* Stores a single object for the producer/consumer pattern and takes care
* of thread synchronization. A first thread, the producer, can put an object
* into the cubby hole using set
. If there already is an object
* in the cubby hole then it is discarded. Meanwhile a second thread, the
* consumer, can get the object using get
. If no object is in
* the cubby hole the consumer will block until one is available.
*
* CubbyHole
is valuable for several situations including where
* a lot of information is produced and consumption is time consuming. For
* example an application that does expensive rendering based on location
* events could only render based on the very latest location.
*/
public class CubbyHole
{
/**
* The object stored by the producer. If this is null
* there is nothing to consume.
*/
private Object cubby;
/**
* Called by the producer to put data
into the cubby hole.
* If there was another object stored in the cubby hole it will be
* removed and returned.
*
* This is a thread-safe method that returns immediately. If another
* thread, acting as the consumer, is blocking on get
it
* will start running so long as data
is not null
.
*
* @param data is the information to store in the cubby hole. If
* null
then there is no job and any calls to get
* will block until set
is called again with a non-
* null
object.
* @return The object in the cubby hole replaced by data
* or null
if nothing was stored.
*/
public synchronized Object set (Object data)
{
Object ret = cubby;
cubby = data;
// Unblock a consumer waiting on get().
notifyAll();
return ret;
}
/**
* Called by the consumer to get an object stored in the cubby hole.
* If nothing is stored this thread will block until set
* is called by a different thread.
*
* @return The object stored in the cubby hole by a call to set
.
* This will never return null
.
* @throws InterruptedException if the program is exiting.
*/
public synchronized Object get ()
throws InterruptedException
{
// Block until a job is available.
while ( cubby == null )
{
wait(); // Releases the lock on this when waiting and re-acquires when awaken
}
// Get the data in the cubby hole.
Object ret = cubby;
cubby = null;
return ret;
}
/**
* Looks at the cubby hole without removing the object from it. This
* is a non-blocking method.
*
* @return The object in the cubby hole which will be null
* if nothing is being stored.
*/
public synchronized Object peek ()
{
return cubby;
}
/**
* Test is the cubby hole is empty. This is a non-blocking method.
*
* @return true
if nothing is in the cubby hole or
* false
if it has an object.
*/
public synchronized boolean empty ()
{
return (cubby == null);
}
}
---------------
package org.j4me.collections;
import org.j4me.*;
import j2meunit.framework.*;
/**
* Tests the CubbyHole
class. It is a thread synchronization
* helper that stores exactly one object. A worker thread can get the very
* latest information stored by a producer.
*
* @see org.j4me.collections.CubbyHole
*/
public class CubbyHoleTest
extends J4METestCase
{
public CubbyHoleTest ()
{
super();
}
public CubbyHoleTest (String name, TestMethod method)
{
super( name, method );
}
public Test suite ()
{
TestSuite suite = new TestSuite();
suite.addTest(new CubbyHoleTest("testBasics", new TestMethod()
{ public void run(TestCase tc) {((CubbyHoleTest) tc).testBasics(); } }));
suite.addTest(new CubbyHoleTest("testBlocking", new TestMethod()
{ public void run(TestCase tc) {((CubbyHoleTest) tc).testBlocking(); } }));
return suite;
}
/**
* Tests that a cubby hole stores exactly one object. Thread synchronization
* is not covered by this test case.
*/
public void testBasics ()
{
try
{
CubbyHole cubby = new CubbyHole();
// Very there is nothing in the empty cubby hole.
boolean isEmpty = cubby.empty();
assertTrue("The cubby hole is empty.", isEmpty);
Object peek = cubby.peek();
assertNull("Nothing comes from peaking into an empty cubby hole.", peek);
// Put something into the cubby hole.
Integer i = new Integer( 13 );
cubby.set( i );
isEmpty = cubby.empty();
assertFalse("The cubby hole has something in it.", isEmpty);
peek = cubby.peek();
assertSame("The cubby hole correctly stored our object.", i, peek);
Object get = cubby.get();
assertSame("Got the object stored in the cubby.", i, get);
// The cubby hole should once again be empty.
isEmpty = cubby.empty();
assertTrue("The cubby hole is empty again.", isEmpty);
peek = cubby.peek();
assertNull("Nothing comes from peaking into the empty again cubby hole.", peek);
// Put several objects into the cubby hole before taking one out.
Integer i1 = new Integer( 1 );
Integer i2 = new Integer( 2 );
Integer i3 = new Integer( 3 );
get = cubby.set( i1 );
assertNull("Nothing returned from empty cubby hole.", get);
get = cubby.set( i2 );
assertSame("Old data i1 returned from cubby hole.", i1, get);
get = cubby.set( i3 );
assertSame("Old data i2 returned from cubby hole.", i2, get);
get = cubby.get();
assertSame("Newest data is in cubby hole.", i3, get);
}
catch (InterruptedException e)
{
fail( e.toString() );
}
}
/**
* Tests that a consumer thread blocks waiting for a producer to add
* something to the cubby hole.
*/
public void testBlocking ()
{
final CubbyHole one = new CubbyHole();
final CubbyHole two = new CubbyHole();
class Consumer extends Thread
{
public void run ()
{
try
{
// Block waiting for something in the first cubby hole.
Object consume = one.get();
// The producer thread should be blocking waiting for
// this thread to put something into the second cubby hole.
two.set( consume );
}
catch (Throwable t)
{
fail( t.toString() );
}
}
}
try
{
// Create a consumer thread.
Consumer consumer = new Consumer();
consumer.start();
// Give up the CPU to let the consumer start and block.
Thread.sleep( 0 );
// Put some data into the first cubby hole to unblock the consumer.
Integer data = new Integer( 13 );
one.set( data );
// Get data from the second cubby hole. This thread will block
// until the consumer puts something into it.
Integer result = (Integer)two.get();
// Verify the consumer thread read our original data from the
// first cubby hole and put it into the second.
assertSame("Data integrety verified.", data, result);
}
catch (InterruptedException e)
{
fail( e.toString() );
}
}
}