@Test
  public void testAsynchronousClose() throws IOException, InterruptedException {
    RegularFile file = regularFile(10);
    final FileChannel channel = channel(file, READ, WRITE);

    file.writeLock().lock(); // ensure all operations on the channel will block

    ExecutorService executor = Executors.newCachedThreadPool();

    List<Future<?>> futures = queueAllBlockingOperations(channel, executor);

    // ensure time for operations to start blocking
    Uninterruptibles.sleepUninterruptibly(10, MILLISECONDS);

    channel.close();

    for (Future<?> future : futures) {
      try {
        future.get();
        fail();
      } catch (ExecutionException expected) {
        assertTrue(expected.getCause() instanceof AsynchronousCloseException);
      }
    }
  }
  @Test
  public void testCloseByInterrupt() throws IOException, InterruptedException {
    RegularFile file = regularFile(10);
    final FileChannel channel = channel(file, READ, WRITE);

    file.writeLock().lock(); // ensure all operations on the channel will block

    ExecutorService executor = Executors.newCachedThreadPool();

    final CountDownLatch latch = new CountDownLatch(1);
    final AtomicReference<Throwable> interruptException = new AtomicReference<>();

    // This thread, being the first to run, will be blocking on the interruptible lock (the byte
    // file's write lock) and as such will be interrupted properly... the other threads will be
    // blocked on the lock that guards the position field and the specification that only one method
    // on the channel will be in progress at a time. That lock is not interruptible, so we must
    // interrupt this thread.
    Thread thread =
        new Thread(
            new Runnable() {
              @Override
              public void run() {
                try {
                  channel.write(ByteBuffer.allocate(20));
                  latch.countDown();
                } catch (Throwable e) {
                  interruptException.set(e);
                  latch.countDown();
                }
              }
            });
    thread.start();

    // ensure time for thread to start blocking on the write lock
    Uninterruptibles.sleepUninterruptibly(5, MILLISECONDS);

    List<Future<?>> futures = queueAllBlockingOperations(channel, executor);

    // ensure time for operations to start blocking
    Uninterruptibles.sleepUninterruptibly(10, MILLISECONDS);

    // interrupting this blocking thread closes the channel and makes all the other threads
    // throw AsynchronousCloseException... the operation on this thread should throw
    // ClosedByInterruptException
    thread.interrupt();

    latch.await();
    assertTrue(interruptException.get() instanceof ClosedByInterruptException);

    for (Future<?> future : futures) {
      try {
        future.get();
        fail();
      } catch (ExecutionException expected) {
        assertTrue(expected.getCause() instanceof AsynchronousCloseException);
      }
    }
  }