What is GCDExecutor?

GCDExecutor and GCDExecutorService are repectively Java Executor and ExecutorService implementations that run tasks by passing them to Apple's Grand Central Dispatch.

In short these classes allow Java programs to use Grand Central Dispatch.

GCDExecutor:

  • offers only one method execute()
  • runs the provided Runnable by calling the C API for Grand Central Dispatch
  • Consists of a single class GCDExecutor and a small JNI library (libGCDExecutor)

GCDExecutorService:

This provides a choice between a very basic API (GCDExecutor) that only requires the addition of 2 files to a project and a more fully featured API that introduces a much larger set of dependencies (GCDExecutorService).

Requirements

  • GCD is only available on Mac OS X 10.6 Snow Leopard.
  • This code has only been run in Java 6 on Snow Leopard.

License

GCDExecutor is licensed under an MIT license.

Rococoa and GCDExecutorService are licensed under the Lesser Gnu Public License.

Caveats

As of October 2009, this code is very new and relatively untested. Use at your own risk! Please send feedback and patches to the Rococoa mailing list.

Download

GCDExecutor is available for download.

For GCDExecutorService, check out the latest Rococoa from Subversion.


Background

Java already has ThreadPoolExecutor, so why use GCDExecutorService?

There are a lot of similarities between Grand Central Dispatch and java.util.concurrent thread pools but GCD offers some advantages worth considering:

1. Java's Thread Pools typically require at least one parameter must be supplied, e.g. the number of threads to use:

Executor e = Executors.newFixedThreadPool(5);

Unbounded pools also exist, but regardless of whether limit is chosen, how can an application programmer know the right number? The best number of threads depends on at least:

  • the computer an application is running on (think MacMini vs. XServe)
  • how busy that system is at any given moment.

GCD can make dynamic decisions about how to allocate CPU resources system wide while a program is actually running, based on the capabilities of the computer and how busy it is.

Enterprising developers could implement similar metrics for themselves, but getting a global perspective on load is difficult from inside the JVM. Letting Apple engineers do it leaves the application programmer free to focus on improving their application.

2. Apple has done a lot of work, all the way down to the kernel level, to make GCD very efficient.

On this point be aware GCDExecutorService is implemented on top of Rococoa, which bridges Java to Cocoa's NSOperationQueue, which in turn sits on top of GCD itself. All of these layers will add time to the scheduling of tasks. Therefore the size of the work items submitted to GCDExecutorService may need to be larger than in other languages, otherwise the overhead of scheduling may swamp any advantage of using GCD.

GCDExecutor should have less overhead – there are fewer layers of code involved. In the current version, each GCD thread that calls back into Java remains attached to the JVM until it dies. So the 2nd and subsequent callbacks into Java from a given GCD thread should be relatively cheap.

The true costs of either approach are currently pure speculation - it really needs to be measured.

What about blocks?

ExecutorServices in Java accept Runnables and Callables as ways of submitting tasks. When deployed as inner classes these interfaces have many similarities to the block concept Apple has added to C, C++ and Objective-C. Certainly they are good enough to submit useful work to GCD today.

Runnables and Callables are not fully equivalent to blocks, partly because they require more syntax, and partly because they do not capture data in the way blocks do. Another name for blocks is 'Closures'. There's a detailed discussion of Closures in Java at Java World. Be sure to read the later pages to understand the differences between Closures and inner classes. The good news is should Closures ever be added to Java, they would enhance Runnable and Callable and thus work with any ExecutorService.

What if my Java application has to work on other platforms?

Grand Central Dispatch is a Mac OS X feature. However, Apple have open sourced the underlying technology as libdispatch. A version that works on FreeBSD already exists, and discussions about Linux and even Windows ports are underway. Maybe one day GCD will be available everywhere.

The good news is the Java programmer does not need to wait. GCDExecutorService is compatible with other ExecutorServices, so code like this can be written:

Executor myExecutor = isMacOSX() ? new GCDExecutorService() : 
                                   Executors.newFixedThreadPool(10);
myExecutor.execute(someUsefulWork);

i.e. on Mac OS X, use GCDExecutorService, otherwise use Java's built in thread pools.