Developing multi-threaded application for use inside WebSphere Application Server

As per J2EE specification, your enterprise application should not create and start new threads. And if you create a class extending Thread and start it you will notice that the newly created thread looses the J2EE context, Ex. If your trying to call a EJB from the newly created thread then that might not work.

The WebSphere Application Server provides WAS-extension, asynchronous beans that you can use for breaking your long running task into pieces and then run those tasks in parallel. You have option to either wait for all the tasks to finish or just return immediately show response to the client and let the long running task keep running in its parallel thread.

I wanted to learn how the asynchonous beans works so i created a simple project, My simple project has a task that takes 10 seconds to execute(I am just using Thread.sleep(10000)) and i want to execute 3 tasks(you can have more) and once they are finished respond to client. I built a sample to demonstrate how you can run these 3 tasks in parallel, you can download it from here

First i did create a MyWork.java which represents one task like this


package com.webspherenotes.was.wm;
import com.ibm.websphere.asynchbeans.Work;

public class MyWork implements Work{
public void run() {
System.out.println("Starting long running task");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace(System.out);
}
System.out.println("Finished long running task");
}
public void release() {
System.out.println("Inside MyWork.release()");
}
}


The MyWork class implements Work interface which is equivalent to a Thread in core java. The run() method of the MyWork class will have my long running logic and in my case i just have Thread.sleep(10000) there to simulate that this task takes 10 seconds.

Then i created a HelloWorkManagerServlet.java which is a Servlet that has to execute 3 tasks before sending output to the user


package com.webspherenotes.was.wm;

import java.io.IOException;
import java.util.ArrayList;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ibm.websphere.asynchbeans.WorkException;
import com.ibm.websphere.asynchbeans.WorkItem;
import com.ibm.websphere.asynchbeans.WorkManager;

public class HelloWorkManagerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
com.ibm.websphere.asynchbeans.WorkManager wm;

public HelloWorkManagerServlet() {
try {
InitialContext ctx = new InitialContext();

wm = (com.ibm.websphere.asynchbeans.WorkManager)
ctx.lookup("wm/default");
System.out.println("Work Manager " + wm);
} catch (NamingException e) {
e.printStackTrace(System.out);
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println("Entering HelloWorkManagerServlet.doGet()");
response.setContentType("text/html");

try {
long beforeTime = System.currentTimeMillis();
MyWork myWork1 = new MyWork();
MyWork myWork2 = new MyWork();
MyWork myWork3 = new MyWork();

WorkItem workItem1 = wm.startWork(myWork1);
WorkItem workItem2 = wm.startWork(myWork2);
WorkItem workItem3 = wm.startWork(myWork3);

ArrayList workList = new ArrayList();
workList.add(workItem1);
workList.add(workItem2);
workList.add(workItem3);
wm.join(workList, WorkManager.JOIN_AND,100000);
System.out.println("Total time " + (System.currentTimeMillis() -beforeTime));
response.getWriter().println("Hello from Hello WorkManger Servlet, Time to execute " +
(System.currentTimeMillis() -beforeTime));
} catch (WorkException e) {
e.printStackTrace(System.out);
} catch (IllegalArgumentException e) {
e.printStackTrace(System.out);
}
System.out.println("Exiting HelloWorkManagerServlet.doGet()");
}
}


In the init() method of the servlet i am looking up WorkManager object from JNDI tree at wm/default. Then inside the doGet() method i am creating three objects of MyWork class and using them to create 3 WorkItem objects. When you call wm.startWork(myWork1) method, the WAS server will take one thread from WorkManager thread pool and use it for executing the run() method of MyWork class, it wont block the main thread for the run() method to finish.

In my sample application i wanted to wait for all 3 tasks to finish to i created a ArrayList of WorkItems for all these Work objects and called wm.join(workList, WorkManager.JOIN_AND,100000) method, which blocks the main servlet thread until all 3 three tasks are finish i am setting maximum limit of 100000(100 seconds) for all 3 threads to finish.

At the end i am calculating how much time it took to execute all 3 tasks and printing it in output. I tried accessing the HelloWorkspaceManager couple of times and this is the output that i got

[9/17/10 8:23:35:484 PDT] 0000002e SystemOut O Entering HelloWorkManagerServlet.doGet()
[9/17/10 8:23:35:500 PDT] 0000004a SystemOut O Starting long running task
[9/17/10 8:23:35:500 PDT] 00000051 SystemOut O Starting long running task
[9/17/10 8:23:35:500 PDT] 00000052 SystemOut O Starting long running task
[9/17/10 8:23:45:500 PDT] 0000004a SystemOut O Finished long running task
[9/17/10 8:23:45:500 PDT] 00000052 SystemOut O Finished long running task
[9/17/10 8:23:45:500 PDT] 00000051 SystemOut O Finished long running task
[9/17/10 8:23:45:500 PDT] 0000002e SystemOut O Total time 10016
[9/17/10 8:23:45:500 PDT] 0000002e SystemOut O Exiting HelloWorkManagerServlet.doGet()
[9/17/10 8:23:45:515 PDT] 0000002e webcontainer E com.ibm.ws.webcontainer.WebContainer handleRequest SRVE0255E: A WebGroup/Virtual Host to handle /favicon.ico has not been defined.
[9/17/10 8:26:33:468 PDT] 0000002e SystemOut O Entering HelloWorkManagerServlet.doGet()
[9/17/10 8:26:33:468 PDT] 00000051 SystemOut O Starting long running task
[9/17/10 8:26:33:468 PDT] 00000053 SystemOut O Starting long running task
[9/17/10 8:26:33:468 PDT] 00000054 SystemOut O Starting long running task
[9/17/10 8:26:43:468 PDT] 00000051 SystemOut O Finished long running task
[9/17/10 8:26:43:468 PDT] 00000053 SystemOut O Finished long running task
[9/17/10 8:26:43:468 PDT] 00000054 SystemOut O Finished long running task
[9/17/10 8:26:43:468 PDT] 0000002e SystemOut O Total time 10000
[9/17/10 8:26:43:468 PDT] 0000002e SystemOut O Exiting HelloWorkManagerServlet.doGet()


As you can see first time all 3 tasks were finished in the 10016 and second time they were executed in 10000 seconds. Which is much better than executing the tasks in sequence which would have taken at least 30000 seconds.

The WorkManager extension is more flexible, it lets you configure the Thread pool and set maximum size of the pool, ...

5 comments:

Anonymous said...

Hi,
Thanks for such a wonderful article. I dont find the asyncbeans.jar in websphere 7 library, can we do the same mentioned in this article in websphere application server 7 ? please let us know.

Thank you,
Venkat.

Shankha said...

This is an extremely useful post. Thanks a lot for this and would expect some more life savers like these on your blog.
Many thanks, Shankha

Anonymous said...

Amazing post man, thanks a LOT for this

Anonymous said...

Thanks a lot ..Really helpful

Achim Müller said...

Great post, I did save me a lot of time! It also works on WebSphere 7.