code: CDILiquibaseConfig fileLocked(final String id, Callable<CDILiquibaseConfig> action) throws Exception {
          log.info(LogType.LOG, String.format("[id = %s] JVM lock acquired, acquiring file lock", id));
          String lockPath = String.format("%s/schema.liquibase.lock", ROOT_PATH);
  
          File lockFile = new File(lockPath);
          if (!lockFile.exists() && lockFile.createNewFile()) {
              log.info(LogType.LOG, String.format("[id = %s] Created lock file [path='%s'].", id, lockPath));
          }
  
          log.info(LogType.LOG, String.format("[id = %s] Trying to acquire the file lock [file='%s']...", id, lockPath));
  
          CDILiquibaseConfig actionResult = null;
          FileLock lock = null;
          try (
              FileOutputStream fileStream = new FileOutputStream(lockPath);
              FileChannel fileChannel = fileStream.getChannel();
          )
          {
              while (null == lock) {
                  try {
                      lock = fileChannel.tryLock();
                  } catch (OverlappingFileLockException e) {
                      log.fine(LogType.LOG, String.format("[id = %s] Lock already acquired, waiting for the lock...", id));
                  }
                  if (null == lock) {
                      log.fine(LogType.LOG, String.format("[id = %s] Waiting for the lock...", id));
                      Thread.sleep(FILE_LOCK_TIMEOUT);
                  }
              }
              log.info(LogType.LOG, String.format("[id = %s] File lock acquired, running liquibase...", id));
              actionResult = action.call();
              lock.release();
          } catch (Exception e) {
              log.warning(LogType.LOG, e.getMessage(), e);
          }
          return actionResult;
      }
docs: Synchronization among multiple JVM's.