class MultiPointOutputStream {

    public synchronized void cancel() {
        if (requireStreamBlocks == null) return;
        if (canceled) return;
        canceled = true;
        // must ensure sync thread is finished, then can invoke 'ensureSync(true, -1)'
        // in try block, otherwise, try block will be blocked in 'ensureSync(true, -1)' and
        // codes in finally block will not be invoked
        noMoreStreamList.addAll(requireStreamBlocks);
        try {
            if (allNoSyncLength.get() <= 0) return;
            if (syncFuture != null && !syncFuture.isDone()) {
                inspectValidPath();
                OkDownload.with().processFileStrategy().getFileLock().increaseLock(path);
                try {
                    ensureSync(true, -1);
                } finally {
                    OkDownload.with().processFileStrategy().getFileLock().decreaseLock(path);
                }
            }
        } finally {
            // close all output stream.
            for (Integer blockIndex : requireStreamBlocks) {
                try {
                    close(blockIndex);
                } catch (IOException e) {
                    // just ignored and print log.
                    Util.d(TAG, "OutputStream close failed task[" + task.getId()
                            + "] block[" + blockIndex + "]" + e);
                }
            }

            store.onTaskEnd(task.getId(), EndCause.CANCELED, null);
        }
    }

      MultiPointOutputStream(@NonNull final DownloadTask task,
                           @NonNull BreakpointInfo info,
                           @NonNull DownloadStore store,
                           @Nullable Runnable syncRunnable);
    public  MultiPointOutputStream(@NonNull DownloadTask task,
                                  @NonNull BreakpointInfo info,
                                  @NonNull DownloadStore store);

}