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.

3 comments:

CARFIELD said...

Is it thread safe? Ff some other thread put item to queue in the same time, what is the behaviour?

Unknown said...

BlockingQueue is an interfaced for a family of concurrent collections -- you cannot predict if the new item will make it into the work list or not, but nothing surprising happens.

Anonymous said...

take() is a blocking operation if nothing is there. It does require acquiring the lock, but unless someone else owns it then the thread won't block and won't "give up the rest of its timeslice".