The most important new feature for the casual developer of multithreaded applications is the new Executor framework. However, the old way of creating threads in Java was to extend the java.lang.Thread class or to implement the java.lang.Runnable interface and pass it to Thread as argument. In this approach, the task is modeled as a Runnable and you create one or more threads for each task. There were no built in facilities to re-use threads such as thread pools. Additionally, once a task was started, there was no easy way to know when the task completed without implementing the wait/notify mechanism.
Since JDK5, another abstraction for concurrent execution of tasks is the Executor interface.
To use the thread pooling framework, you create an Executor instance, then you pass it some runnable tasks:
Now you can create objects of this executor and run the methods:
Since JDK5, another abstraction for concurrent execution of tasks is the Executor interface.
public interface Executor {
void execute(Runnable cmd) ;
}
To use the thread pooling framework, you create an Executor instance, then you pass it some runnable tasks:
class MyExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
Now you can create objects of this executor and run the methods:
Executor executor = new MyExecutor;
executor.execute(aRunnable1);
executor.execute(aRunnable2);
A java.util.concurrent.Executor is an object that executes Runnable tasks. It is similar to calling
new Thread (aRunnableObject).start ();
The concurrency utilities also include a ThreadPoolExecutor class that offers support for many common pooling operations. For a single new thread, there is perhaps not much reason to use an Executor. However, most multithreaded applications involve several threads. Threads need stack and heap space, and, depending on the platform, thread creation can be expensive. In addition, cancellation and shutdown of threads can be difficult.
The new Executor framework solves all those problems in a way that decouples task submission from the mechanics of how each task will be run, including the details of thread use, scheduling, etc.
An Executor can and should be used instead of explicitly creating threads. For example, rather than creating a new thread and starting it as above, you can use:
Executor executor = some Executor factory method;
exector.execute (aRunnable);
Notice that our Executor object was returned by an Executor factory. (We discuss design patterns like factory methods ; a factory is a standard name for a method that is used to obtain an instance of a class or subclass rather than making it with a constructor.)
There are several static Executor factory methods available in the java.util.concurrent.Executors class. If you have several Runnable tasks to carry out, but do not have specific requirements about the order in which they occur, then a simple thread pool arrangement is provided as follows:
Executor executor = Executors.newFixedThreadPool (5);
executor.execute (new RunnableTask1 ());
executor.execute (new RunnableTask2 ());
executor.execute (new RunnableTask3 ());
...
Here the tasks run and complete under the control of the Executor, which reuses threads from the thead pool as needed without incurring the overhead of always creating new threads. You can stop the threads with
executor.shutdown();
There are several more Executor factories in the Executors class for other needs beyond the scope of this book. Refer to the J2SE 5.0 API docs for complete information.
No comments:
Post a Comment