/**
   * Gets last added explicit lock candidate by thread id and key.
   *
   * @param threadId Thread id.
   * @param key Key to look up.
   * @return Last added explicit lock candidate for given thread id and key or {@code null} if no
   *     such candidate.
   */
  @Nullable
  public GridCacheMvccCandidate explicitLock(long threadId, KeyCacheObject key) {
    if (threadId < 0) return explicitLock(key, null);
    else {
      GridCacheExplicitLockSpan span = pendingExplicit.get(threadId);

      return span == null ? null : span.candidate(key, null);
    }
  }
  /**
   * Gets explicit lock candidate added by any thread by given key and lock version.
   *
   * @param key Key.
   * @param ver Version.
   * @return Lock candidate that satisfies given criteria or {@code null} if no such candidate.
   */
  @Nullable
  public GridCacheMvccCandidate explicitLock(KeyCacheObject key, @Nullable GridCacheVersion ver) {
    for (GridCacheExplicitLockSpan span : pendingExplicit.values()) {
      GridCacheMvccCandidate cand = span.candidate(key, ver);

      if (cand != null) return cand;
    }

    return null;
  }
  /**
   * Checks if given key is locked by thread with given id or any thread.
   *
   * @param key Key to check.
   * @param threadId Thread id. If -1, all threads will be checked.
   * @return {@code True} if locked by any or given thread (depending on {@code threadId} value).
   */
  public boolean isLockedByThread(KeyCacheObject key, long threadId) {
    if (threadId < 0) {
      for (GridCacheExplicitLockSpan span : pendingExplicit.values()) {
        GridCacheMvccCandidate cand = span.candidate(key, null);

        if (cand != null && cand.owner()) return true;
      }
    } else {
      GridCacheExplicitLockSpan span = pendingExplicit.get(threadId);

      if (span != null) {
        GridCacheMvccCandidate cand = span.candidate(key, null);

        return cand != null && cand.owner();
      }
    }

    return false;
  }