code: private static void countFile(String fname) throws Exception {
      // large buffers pay off
      final int bufSize = 50 * 1024 * 1024;
      Queue<Future<WorkUnit>> work = new ArrayDeque<>();
      Path path = Paths.get(new URI(fname));
      long size = Files.size(path);
      System.out.println(fname + ": " + size + " bytes.");
      int nThreads = (int) Math.ceil(size / (double) bufSize);
      if (nThreads > 4) nThreads = 4;
      System.out.println("Reading the whole file using " + nThreads + " threads...");
      Stopwatch sw = Stopwatch.createStarted();
      long total = 0;
      MessageDigest md = MessageDigest.getInstance("MD5");
  
      ExecutorService exec = Executors.newFixedThreadPool(nThreads);
      int blockIndex;
      for (blockIndex = 0; blockIndex < nThreads; blockIndex++) {
        work.add(exec.submit(new WorkUnit(Files.newByteChannel(path), bufSize, blockIndex)));
      }
      while (!work.isEmpty()) {
        WorkUnit full = work.remove().get();
        md.update(full.buf.array(), 0, full.buf.position());
        total += full.buf.position();
        if (full.buf.hasRemaining()) {
          full.close();
        } else {
          work.add(exec.submit(full.resetForIndex(blockIndex++)));
        }
      }
      exec.shutdown();
  
      long elapsed = sw.elapsed(TimeUnit.SECONDS);
      System.out.println("Read all " + total + " bytes in " + elapsed + "s. ");
      String hex = String.valueOf(BaseEncoding.base16().encode(md.digest()));
      System.out.println("The MD5 is: 0x" + hex);
      if (total != size) {
        System.out.println(
            "Wait, this doesn't match! We saw "
                + total
                + " bytes, "
                + "yet the file size is listed at "
                + size
                + " bytes.");
      }
    }
docs: Print the length and MD5 of the indicated file.
  
  <p>This uses the normal Java NIO Api, so it can take advantage of any installed NIO Filesystem
  provider without any extra effort.