code: public E getElementBlocking(long timeoutMillis) throws InterruptedException {
  		if (timeoutMillis == 0L) {
  			// wait forever case
  			return getElementBlocking();
  		} else if (timeoutMillis < 0L) {
  			throw new IllegalArgumentException("invalid timeout");
  		}
  
  		final long deadline = System.nanoTime() + timeoutMillis * 1_000_000L;
  
  		lock.lock();
  		try {
  			while (open && elements.isEmpty() && timeoutMillis > 0) {
  				nonEmpty.await(timeoutMillis, TimeUnit.MILLISECONDS);
  				timeoutMillis = (deadline - System.nanoTime()) / 1_000_000L;
  			}
  
  			if (!open) {
  				throw new IllegalStateException("queue is closed");
  			}
  			else if (elements.isEmpty()) {
  				return null;
  			} else {
  				return elements.removeFirst();
  			}
  		} finally {
  			lock.unlock();
  		}
  	}
docs: Returns the next element in the queue. If the queue is empty, this method
  waits at most a certain time until an element becomes available. If no element
  is available after that time, the method returns null.
  
  <p>The method throws an {@code IllegalStateException} if the queue is closed.
  Checking whether the queue is open and removing the next element is one atomic operation.
  
  @param timeoutMillis The number of milliseconds to block, at most.
  @return The next element in the queue, or null, if the timeout expires  before an element is available.
  
  @throws IllegalStateException Thrown, if the queue is closed.
  @throws InterruptedException Throw, if the thread is interrupted while waiting for an
  element to be added.