|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 5 of 6
public synchronized BigInteger nextPrime()
{
BigInteger result;
int resultIndex = resultsBufferAccessIndex++;
try
{
if (!barrier.isClosed() &&
(resultIndex == resultsBuffer.size()))
barrier.close();
barrier.await();
result = resultsBuffer.get(resultIndex);
}
catch (InterruptedException exce)
{
..........................................
}
catch (IndexOutOfBoundsException exce)
{
//search exhausted
result = null;
}
}
Next, you will examine the PrimeNumberSearcher implementation that returns instances of this handle.
The task of partitioning the search space and assigning contiguous blocks to individual threads is performed by the class
com.javaworld.primefinder.GatedPrimeNumberSearcher; this is an implementation of the contract com.javaworld.primefinder.PrimeNumberSearcher.
On instantiation, an object of this type determines the number of system processors, and uses this to partition the search
space into buckets or blocks of near uniform size, which in turn are assigned to individual tasks. The bucketing information
is encapsulated in the com.javaworld.primefinder.PartitionInfo class, which is shown in Listing 8.
class PartitionInfo
{
PartitionInfo(int numberOfBuckets, long bucketSize)
public int getNumberOfBuckets().....
public long getBucketSize() ........
}
Instances of this class are created by the method in Listing 9, which is defined in the task/search-thread implementation
com.javaworld.primefinder.GatedPrimeNumberSearcher.
private PartitionInfo getPartitionInfo(long lowerBound,
long upperBound)
{
PartitionInfo result;
int proposedBucketCount = numberOfProcessors;
long bucketSize = (upperBound-lowerBound) /
proposedBucketCount;
result = new PartitionInfo(proposedBucketCount,
bucketSize);
return result;
}
As you can see in Listing 10, instances of PartitionInfo are used to create one or more search threads -- or, more accurately, one or more runnable tasks/targets, as implemented
by the class com.javaworld.primefinder.PrimeSearchThread. In addition to information about its search range, each thread also holds a reference to the result handle, thus allowing
it to write search results to the handle's internal result buffer as and when they become available.
public PrimeNumberSource findPrimeNumbers(BigInteger aLowerBound,
BigInteger aUpperBound)
{
if (aUpperBound.longValue()<=aLowerBound.longValue())
throw new IllegalArgumentException("Upperbound must be
greater than lowerbound");
long lowerBound = aLowerBound.longValue(),
upperBound = aUpperBound.longValue();
final ConcurrentPrimeNumberSource result;
PartitionInfo partitionInfo =getPartitionInfo(lowerBound,
upperBound);
result = new ConcurrentPrimeNumberSource(
partitionInfo.getNumberOfBuckets());
Thread searchThread;
long shiftingLowerBound = lowerBound,
shiftingUpperBound = partitionInfo.getBucketSize();
while (shiftingUpperBound <= upperBound)
{
searchThread =
new Thread(new PrimeSearchThread(
BigInteger.valueOf(shiftingLowerBound),
BigInteger.valueOf(shiftingUpperBound),
result));
searchThread.start();
shiftingLowerBound = shiftingUpperBound;
shiftingUpperBound+= partitionInfo.getBucketSize();
}
return result;
}
Before you can examine the test unit, there remains one application class to mention: the consumer/reader thread implementation
com.javaworld.primefinder.PrimeNumberReader, whose core method is shown in Listing 11.
java.util.concurrent that can be used to speed up time-consuming tasks in multithreaded Java applications.