Friday, September 14, 2007

Running "N" foreground tasks in Java

A handy helper method for running "N" foreground tasks in Java with a timeout. A more sophisticated technique would permit examination of job status:

/**
 * Invokes <var>n</var> copies of the given <var>runnable</var>, timing out
 * after <strong>10 unit</strong> with {@code InterruptedException}.
 *
 * @param n the number of threads to run
 * @param runnable the thread body to execute
 * @param timeout the timeout
 * @param unit the timeout unit
 *
 * @throws InterruptedException if timed out
 */
public static void invokeNCopiesWithTimeout(final int n,
        final Runnable runnable, final long timeout, final TimeUnit unit)
        throws InterruptedException {
    final ExecutorService pool = newFixedThreadPool(n);

    pool.invokeAll(
            Collections.<Callable<Void>>nCopies(n, new Callable<Void>() {
                public Void call() {
                    runnable.run();
                    return null;
                }
            }));

    pool.awaitTermination(timeout, unit);
}

I find this particularly useful for blackbox concurrency testing. I run, say, 100 threads which both read and write to a common data structure, and accept the lack of exceptions as empirical evidence of safeness. (In truth, it only provides a comfort zone, not a proof. Reasoning about threads is difficult when maintaining disparate interacting components.)

Monday, September 10, 2007

Performing fixed amounts of work with blocking queues

A colleague of mine showed me a nice idiom with BlockingQueue for performing a fixed amount of work:

final List<T> work = new ArrayList<T>(N);

for (;;) {
    // Wait until there is some work
    work.add(queue.take());
    // Get more work, if available
    queue.drainTo(work, N - 1);

    for (final T item : work)
        process(item);

    work.clear();
}

This idiom works on 1 to N chunks at a time efficiently and succinctly.

An example use is batching SQL updates. At the top of the loop begin the batch transaction after take() and finish the batch after the processing loop.

UPDATE: This idiom is also more efficient when unbounded amounts of work:

queue.drainTo(work);

Every call to take() is blocking and checks a lock — essentially giving up any remaining thread time slice. Using drainTo(work) avoids this lost thread work, cutting down on lock checks.