Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

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

Java concurrency with thread gates

Manage concurrent thread execution in complex business applications

  • Print
  • Feedback

Page 5 of 6

Listing 7. The implementation of the nextPrime() method

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 search executor

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.

Listing 8. Encapsulation of search-space partition information

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.

Listing 9. Method that partitions the search space into blocks

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.

Listing 10. Implementation of contract findPrimeNumbers()

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.

  • Print
  • Feedback

Resources

More from JavaWorld