code: public synchronized void close() throws IOException {
      if (!mClosed.get()) {
        // Setting closed should be the first thing we do because if any of the close operations
        // fail we'll only have a half-closed object and performing any more operations or closing
        // again on a half-closed object can possibly result in more errors (i.e. NPE). Setting
        // closed first is also recommended by the JDK that in implementations of #close() that
        // developers should first mark their resources as closed prior to any exceptions being
        // thrown.
        mClosed.set(true);
        mWorkerGroup.shutdownGracefully(1L, 10L, TimeUnit.SECONDS);
        mFileSystemMasterClientPool.close();
        mFileSystemMasterClientPool = null;
        mBlockMasterClientPool.close();
        mBlockMasterClientPool = null;
        mMasterInquireClient = null;
        for (BlockWorkerClientPool pool : mBlockWorkerClientPool.values()) {
          pool.close();
        }
        mBlockWorkerClientPool.clear();
  
        if (mMetricsMasterClient != null) {
          ThreadUtils.shutdownAndAwaitTermination(mExecutorService,
              mClientContext.getConf().getMs(PropertyKey.METRICS_CONTEXT_SHUTDOWN_TIMEOUT));
          mMetricsMasterClient.close();
          mMetricsMasterClient = null;
          mClientMasterSync = null;
        }
        mLocalWorkerInitialized = false;
        mLocalWorker = null;
      } else {
        LOG.warn("Attempted to close FileSystemContext with app ID {} which has already been closed"
            + " or not initialized.", mAppId);
      }
    }
docs: Closes all the resources associated with the context. Make sure all the resources are released
  back to this context before calling this close. After closing the context, all the resources
  that acquired from this context might fail. Only call this when you are done with using
  the {@link FileSystem} associated with this {@link FileSystemContext}.