devxlogo

Build Java Apps that Can Multitask

Build Java Apps that Can Multitask

f you worked with thread programming in C or C++ or prior versions of Java, you know what a headache managing the threads in your code can be. In single-threaded programs, a bug in the code causes an application to fail at the same point each and every time. In multithreaded programs, however, the failure occurs only when certain conditions are met. Because predicting all the conditions that can cause application failure is so difficult, thread programming is challenging. Some programmers avoid the challenge all together, while others?savvy problem-solvers?stay glued to their computers until they get it right.

The J2SE 5.0 platform includes a new package of concurrency utilities. The classes in this package are building blocks for concurrent classes or applications used in concurrent designs. The concurrency utilities include the following:

  • A high-performance, flexible thread pool
  • A framework for asynchronous execution of tasks
  • A host of collection classes optimized for concurrent access

This article introduces the J2SE 5.0 framework classes and their important features. The accompanying downloadable code provides simple and easy-to-use examples that demonstrate all the new thread framework classes. Running the examples while you read the article gives you a better understanding of each feature.

Executor Framework
The Executor framework provides simple standardized extensible classes that offer useful functionality that otherwise is tedious or difficult to implement manually. This framework standardizes invocation, scheduling, and execution. It provides support for controlling asynchronous tasks according to a set of execution policies.

The Executor interface executes submitted runnable tasks. It provides a way to decouple task submission from the mechanics of task execution. Programmers normally use an Executor instead of explicitly creating threads. The Executor interface also offers both synchronous and asynchronous execution of tasks.

For synchronous execution, use the following commands:

Class MySynExecutor implements Executor{	public void execute(Runnable r) {        	r.run();     	}}

For asynchronous execution, use the following commands:

Class MyASynExecutor implements Executor{	public void execute(Runnable r) {        	new Thread(r).start();     	}}

ExecutorService Class
The ExecutorService class provides methods for managing termination and tracking the progress of one or more asynchronous tasks. The MyExecutorService.java file in the accompanying code download demonstrates the process of managing termination. It initiates a ThreadPool of size three and adds the threads sequentially. When the number of threads reaches the pool size, it invokes the shutdown method. After calling the shutdown() method, the pool does not accept any new tasks for execution. After waiting 10 seconds, the pool invokes shutDownNow(). This method makes a best effort to terminate all running tasks. In the example the application fails to terminate the executing threads.

ScheduledExecutorService
The ScheduledExecutorService class is my favorite. It is very handy for scheduling tasks that run periodically, which is especially useful for cleanup jobs (e.g., cleaning up all the temp files created by your application, etc.).The MyScheduledExecutorService.java file demonstrates the scheduling process by printing beep every five seconds:

final Runnable beeper = new Runnable() {		public void run() { System.out.println("beep"); }};final ScheduledFuture beeperHandle =scheduler.scheduleAtFixedRate(beeper, 1, 5, SECONDS); 

Future and FutureTask
In earlier versions of Java, it is very difficult to find the status of an executing thread and make the thread return a value after execution. Because the run method returns void, you need to write a lot of code to get a return value from the thread. Programmers who tried this approach understand the ordeals associated with it.

Using the Future interface or the FutureTask class you can get a return value from the asynchronous execution of the thread. The Future interface provides methods to check if the computation is complete, retrieve the results of the computation, or cancel the computation. FutureTask provides the basic implementation of the Future interface methods. The results can be retrieved only when the computations have completed; if the computation is not completed, the get method will block.

The MyStringReverser.java file demonstrates the use of the FutureTask class with an easy-to-understand example. It reverses the submitted string slowly at a rate of one char per second. In the mean time, the main thread keeps polling to see if the task is completed:

while(!future.isDone()){ System.out.println("Task not yet completed."); try{	 Thread.currentThread().sleep(500); }catch(InterruptedException ie){ System.out.println("Will check after 1/2 sec."); }}

After the task is completed, the result is retrieved from the Future object using the get method:

System.out.println("Here is result..."+future.get());

ThreadPoolExecutor
With the ThreadPoolExecutor class, you can write your own server. This class provides many features for configuring and tuning it, similar to many large-scale enterprise EJB servers. The following are a few of its configurable parameters:

  • Core and maximum poolsizes

By setting corePoolSize and maximumPoolSize to the same value, you create a fixed-size thread pool. By setting maximumPoolSize to an essentially unbounded value such as Integer.MAX_VALUE, you allow the pool to accommodate an arbitrary number of concurrent tasks.

  • On-demand construction

By default, ThreadPoolExecutor initially creates and starts even core threads only when new tasks need them, but you can override this dynamically using prestartCoreThread or prestartAllCoreThreads.

  • Keep-alive times

If the pool currently has more than corePoolSize threads, excess threads will be terminated if they have been idle for more than the keepAliveTime.

  • Queuing

Queuing follows the following rules:

  • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
  • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
  • Hook methods

This class provides beforeExecute() and afterExecute() hook methods that are called before and after execution of each task. In order to use them, you must sub-class the class (as these methods are protected).

MyThreadPoolExecutor.java provides detailed examples to monitor various configured parameters. You can uncomment the code to see for yourself how the pool and queue sizes vary with the addition and completion of each task. You also can play with the value settings in the code.

Concurrent Collections
JDK 1.5 provides the following Collection implementations that are designed for use in multithreaded contexts:

The ConcurrentHashMap class provides full thread-safe concurrency support for retrievals and adjustable expected concurrency for updates. CopyOnWriteArraySet is a thread-safe variant of set, and CopyOnWriteArrayList is a thread-safe variant of ArrayList. Each makes a copy of the underlying array or sets it before modifying the original. As a result, the reads are fast but the updates are slow.

Concurrent collection classes provide snapshot-style data to the Iterators (even when the underlying data is changed, it is not reflected in the Iterators).

Synchronizers
JDK 1.5 provides advanced classes like Semaphore, CountDownLatch, CyclicBarrier, and an Exchanger for special-purpose synchronization. A detailed analysis and usage of these classes is beyond the scope of this article, as it requires some academic background.

With this new set of classes, you can convince your thread-scared, technical boss to develop multithreaded applications.

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist