Welcome Guest!
Create Account | Login
Locator+ Code:

Search:
FTPOnline
Channels Conferences Resources Hot Topics Partner Sites Magazines About FTP RSS 2.0 Feed

Free Subscription to Java Pro

email article
printer friendly

Put JDK 1.5's Executor to Work for You
Take a look at a powerful, scalable, and extensible framework you can use to execute and manage asynchronous tasks
by Brian Goetz

August 4, 2004

The new java.util.concurrent package that is part of JDK 1.5 (Tiger) contains a host of concurrency utilities that are designed to make developing concurrent applications easier. The package provides the Executor framework, which is a collection of classes for managing asynchronous task execution. One way to execute a task asynchronously is to simply fire off a new thread:

new Thread(aRunnable).start();
ADVERTISEMENT

While this approach is convenient, it has a number of serious drawbacks. Thread creation on some platforms is relatively expensive, and starting a new thread for each task could limit throughput. Even if thread creation were free, in a server application, or any application that will likely be executing many asynchronous tasks, there is no way to bind the number of threads created. This situation can cause the application to crash with OutOfMemoryException, or perform poorly because of resource exhaustion. A stable server application needs a policy to govern how many requests it can be processing at once. A thread pool is an ideal way to manage the number of tasks executing concurrently.

The Executor framework simplifies the management of asynchronous tasks and decouples task submission and management from task execution. The various implementations of Executor in java.util.concurrent can support thread-per-task execution, a single background thread for executing tasks, or thread pooling (and it would even be possible to write an Executor that executes the Runnable in another Java Virtual Machine). It can provide for execution policies such as before-execution and after-execution hooks, saturation policies, queuing policies, and so on. To submit a task to an Executor, you simple call the execute() method:

executor.execute(aRunnable);

Because executor is an object that is instantiated somewhere else (often at program startup), it becomes possible to change execution policies later without changing any of the code that submits tasks.

Many developers have implemented their own thread pools to manage task execution. My advice to those developers is just throw them away; it is almost certainly the case that the thread pool implementation in java.util.concurrent is more robust, error free, scalable, flexible, and better performing than the one you are using currently.

Add Life-Cycle Management
The Executor interface is quite simple. It includes only the execute() method, but if you are building server applications some additional features are needed to provide a useful task-execution service for your application. The ExecutorService interface extends Executor, adding life-cycle management features such as orderly shutdown and other useful functionality.

In this code for a simple Web server application, which listens on port 80, when a request comes in, it fires off a thread to service the request:

class WebServer {
  public static void main(
    String[] args) {
    ServerSocket socket = 
      new ServerSocket(80);
    while (true) {
      final Socket connection = 
        socket.accept();
      Runnable r = 
        new Runnable() {
        public void run() {
          handleRequest(
            connection);
        }
      };
      // Don't do this! 
      new Thread(r).start(); 
    }
  }
}



Back to top













Java Pro | Visual Studio Magazine | Windows Server System Magazine
.NET Magazine | Enterprise Architect | XML & Web Services Magazine
VSLive! | Thunder Lizard Events | Discussions | Newsletters | FTP Home