class LockFile implements Closeable {

  public static LockFile lock(Path lockFile) throws IOException {
    try {
      // This first lock is to prevent multiple threads from calling FileChannel.lock(), which would
      // otherwise throw OverlappingFileLockException
      lockMap.computeIfAbsent(lockFile, key -> new ReentrantLock()).lockInterruptibly();

    } catch (InterruptedException ex) {
      throw new IOException("Interrupted while trying to acquire lock", ex);
    }

    Files.createDirectories(lockFile.getParent());
    FileOutputStream outputStream = new FileOutputStream(lockFile.toFile());
    FileLock fileLock = null;
    try {
      fileLock = outputStream.getChannel().lock();
      return new LockFile(lockFile, fileLock, outputStream);

    } finally {
      if (fileLock == null) {
        outputStream.close();
      }
    }
  }

  private  LockFile(Path lockFile, FileLock fileLock, OutputStream outputStream);

}