Leveraging EJB Timers for J2EE Concurrency : Page 2

Improve the performance of your J2EE applications by using a concurrency approach that takes advantage of the inherent thread pool management of EJB timers.

EJB Concurrency Example: Fibonacci
The example concurrency application uses the Fibonacci numbers, a common mathematical formula where each element in the sequence is the sum of the previous two numbers. You can formulate it as follows (more efficient formulations would not grow exponentially with respect to n, but this one will suffice for the example):

f(n) = 0, if n=0
f(n) = 1, if n=1
f(n) = f(n-1) + f(n-2), if n>1

This produces a sequence that looks like this:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144 ...

Suppose your requirement is to implement an EJB Fibonacci calculator that can compute, in parallel, the Fibonacci results for a set of numbers. You first must implement a task that calculates the Fibonacci result for a given number. Here's one possible implementation of your FibonacciTask:

public class FibonacciTask extends AbstractTask
{
private intn;
private longresult;
public FibonacciTask(int n){
this.n = n;
}
public void run()
{
result = fibonacci(n);
}
private long fibonacci(int n){
long ret = n;
if (n > 1)
ret = fibonacci(n-1) + fibonacci(n-2);
return ret;
}
public int getN(){
returnn;
}
public long getResult()
{
returnresult;
}
}

The run() method simply calls the fibonacci(n) method and stores the result in the class field. The fibonacci(n) method is a recursive implementation of the formula described earlier.

The next step is to implement the calculateFibonacci() method in FibonacciCalculatorEJB. This method will instantiate the appropriate FibonaaciTask(s) and submit them to the EJBTimerConcurrencyManager for execution. To inject the EJBTimerConcurrencyManager into the ConcurrencyManager variable, you can use the EJB3 @EJB annotation as follows:

The calculateFibonacci method takes as argument an array of numbers whose Fibonacci needs to be computed. This example initially schedules the tasks (one for each Fibonacci number) using the blocking scheduling method (executeAndWait with no timeout) that returns only when all Fibonacci results are calculated. The elapsed time is printed along with the result:

public void calculateFibonacci(int[] numbers)
{
ArrayList<Task> tasks = new ArrayList<Task>();
for (int i = 0; i < numbers. length; i++)
tasks.add(new FibonacciTask(numbers[i]));
long initialTime = System.currentTimeMillis();
concurrencyManager.executeAndWait(tasks);
long elapsedTime = System.currentTimeMillis() - initialTime;
printFibonacciResults(tasks,elapsedTime);
}

The following is the printFibonacciResults method implementation:

private void printFibonacciResults(Collection<Task> tasks, long elapsedTime)
{
logger.info("** Completed Fibonacci Computations in " + elapsedTime + "ms **");
for (Task task : tasks){
FibonacciTask ft = (FibonacciTask) task;
logger.info("Fibonacci(" + ft.getN() + ") = " + ft.getResult());
}
}

Calling the calculateFibonacci method with the numbers 1, 7, 20, 31, and 35 produces the following (the elapsed time will vary depending on your hardware configuration):

16:42:41,759 INFO [FibonacciCalculatorEJB] ** Completed Fibonacci Computations in 381ms **
16:42:41,759 INFO [FibonacciCalculatorEJB] Fibonacci(1) = 1
16:42:41,759 INFO [FibonacciCalculatorEJB] Fibonacci(7) = 13
16:42:41,759 INFO [FibonacciCalculatorEJB] Fibonacci(20) = 6765
16:42:41,759 INFO [FibonacciCalculatorEJB] Fibonacci(31) = 1346269
16:42:41,759 INFO [FibonacciCalculatorEJB] Fibonacci(35) = 9227465

As you can see, parallelizing the tasks using the concurrency framework is relatively straightforward. One factor to consider in this example is the exponential growth of Fibonacci computation time as n increases. You can provide a timeout parameter to the concurrency manager's executeAndWait method to specify the maximum time you wish to wait. The executeAndWait method returns the set of completed tasks at timeout:

public void calculateFibonacci(int [] numbers, long timeout)
{
ArrayList<Task> tasks = new ArrayList<Task>();
for (int i = 0; i < numbers.length; i++)
tasks.add(new FibonacciTask(numbers[i]));
long initialTime = System.currentTimeMillis();
Collection<Task> completedTasks = concurrencyManager.executeAndWait(tasks, timeout);
long elapsedTime = System.currentTimeMillis() - initialTime;
printFibonacciResults(completedTasks, elapsedTime);
}

Calling the method with the numbers 1, 4, 9, 24, and 40 and a timeout of 300ms produces the following:

16:42:42,089 INFO [FibonacciCalculatorEJB] ** Completed Fibonacci Computations in 310ms **
16:42:42,089 INFO [FibonacciCalculatorEJB] Fibonacci(1) = 1
16:42:42,089 INFO [FibonacciCalculatorEJB] Fibonacci(4) = 3
16:42:42,089 INFO [FibonacciCalculatorEJB] Fibonacci(9) = 34
16:42:42,089 INFO [FibonacciCalculatorEJB] Fibonacci(24) = 46368

The output indicates that the executeAndWait returned after 310ms (the extra 10ms is due to overhead) without waiting for Fibonacci(40) to complete.

The following section discusses the implementation details of the EJB Timer-based concurrency framework. It covers the implementation of the non-blocking execute method first, and then discusses the blocking style of execution.