Alternative to Thread.sleep()

by Michael

I stumbled upon this in our code base.

long slept = 0;

while (_queue.size() < minCount && slept < timeout) {
  Thread.sleep(100);
  slept += 100;
}

if (_queue.size() < minCount || slept > timeout)
  return new ArrayList<Delivery>();

return drain(timeout - slept);

A thread (publisher) publishes to the queue while another thread (consumer) attempts to drain the queue if the size of the queue is above some threshold. If the queue is below said threshold, the consumer thread will sleep for 100ms and check the size again. If the total sleep time has exceeded some timeout value, we return an empty list. In all other cases we drain the queue into a list and return the list.

There is a cleaner way to do this and I think most situations where we sleep for some interval and then check some value can be rewritten to use Reentrant locks with Conditions.

// consume method //

long elapse = timeout + System.currentTimeMillis();

drainLock.lock();
try {
  while (_queue.size() < minCount && System.currentTimeMillis() < elapse)
     newDelivery.await(timeRemaining(elapse), TimeUnit.MILLISECONDS);
} finally {
  drainLock.unlock();
}

if (_queue.size() < minCount)
  return new ArrayList<Delivery>();

return drain(timeRemaining(elapse));

// publish method //

this._queue.add(new Delivery(envelope, properties, body));

drainLock.lock();
try {
  newDelivery.signal();
} finally {
  drainLock.unlock();
}

The JavaDoc is not trivial to understand. One would think that the consumer holding the lock would block the publisher from publishing to the queue. The explanation is that if a thread is holding on to a lock and waiting, another thread can grab the lock too. So in the example above, when the consumer thread calls await(), it allows another thread, in this case the publisher thread, to grab the lock and call signal(). The signal() method awakens the waiting consumer and allows the consumer tread to run once the lock is released by the publisher.

Advertisements