5

在 Java 线程中,您可以在一个列表中有一些线程,启动它们,然后有一个主线程join,然后是另一个,等待所有进程完成,然后再继续。

在其他模型中,我不确定你会如何做到这一点。以RootTools 3.0 Command 类为例。您创建了一个Command具有三个方法的 , commandOutput, commandFinished,commandTerminated并且您可以在进程结束时使用回调来执行某些操作,但我不知道您将如何等待多个进程(例如,浏览多个目录的列表并对文件大小求和)。

我相信 Android Asynctask 也会有类似的问题——你可以轻松地进行回调,但没有办法等待多个任务。除非我错过了什么?

4

3 回答 3

3

##介绍

我已经为我以前的一个项目浏览过这个主题,并找到了不同的解决方案。我最终为该项目使用了第一种方法,因为它最适合该特定项目。

让我们ImageDownloader成为一个从 URL 异步下载图像的类,它具有以下属性。

  • 一个接口 -ImageDownloadCallback在任务完成时获取回调。它有两种方法
  • void onSuccess(String imagePath): 任务成功完成时调用。
  • void onFailure():当任务未能完成时调用。
  • 一种方法——download(String url, ImageDownloadCallback callback)启动下载任务

PauseModeCallbackHandler,ChainModeCallbackHandlerParallelModeCallbackHandler分别是这三个方法的回调的包装类。您可以根据要执行的任务自定义它们。




##方法一: 通过暂停启动线程,一个一个地执行任务。

解决方案 1 的插图

优点
在原始线程中获取结果

缺点
需要让线程等待


###ThreadLockedTask 可以使用这个类让线程一直等待,直到得到结果。
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author Ahamad Anees P.A
 * @version 1.0
 * @param <T> type
 */
public class ThreadLockedTask<T> {

    private AtomicReference<ResultWrapper<T>> mReference;

    public ThreadLockedTask() {
        mReference = new AtomicReference<>(new ResultWrapper<T>());
    }

    public T execute(Runnable runnable) {
        runnable.run();
        if (!mReference.get().mIsSet)
            lockUntilSet();
        return mReference.get().mResult;
    }

    private void lockUntilSet() {
        synchronized (this) {
            while (!mReference.get().isSet()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    public void setResult(T result) {
        synchronized (this) {
            ResultWrapper<T> wrapper = mReference.get();
            wrapper.setResult(result);
            wrapper.setIsSet(true);
            notify();
        }
    }

    public static class ResultWrapper<T> {
        private boolean mIsSet;
        private T mResult;

        public boolean isSet() {
            return mIsSet;
        }

        public T getResult() {
            return mResult;
        }

        void setIsSet(boolean isCompleted) {
            this.mIsSet = isCompleted;
        }

        void setResult(T result) {
            this.mResult = result;
        }
    }

}

###样本

import java.util.ArrayList;
import java.util.List;

public class PauseModeCallbackHandler {

    // List of results
    private static List<String> results;

    public static void start(final List<String> urls, final ImageDownloader.ProgressUpdateListener listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                results = new ArrayList<>();

                // Do tasks one by one
                for (final String url :
                        urls) {

                    //Here the result is a String. Change "String" in the following two lines for other datatypes.
                    final ThreadLockedTask<String> task = new ThreadLockedTask<>();
                    final String imagePath = task.execute(new Runnable() {
                        @Override
                        public void run() {
                            //Start the task here
                            ImageDownloader.getInstance(listener).download(url,
                                    new ImageDownloader.ImageDownloadCallback() {
                                        @Override
                                        public void onSuccess(String imagePath) {
                                            //Set the result on success
                                            task.setResult(imagePath);
                                        }

                                        @Override
                                        public void onFailure() {
                                            //Set result as null on failure
                                            task.setResult(null);
                                        }
                                    });
                        }
                    });

                    if (imagePath!=null)
                        results.add(imagePath);

                }

                afterCallbacks();
            }
        }).start();
    }

    private PauseModeCallbackHandler() {}

    private static void afterCallbacks() {
        // All tasks completed. List "results" now holds the result

        DemoActivity.isTasksInProgress = false;
    }
}




##方法2: 从前一个回调的回调中执行任务,就像连锁反应一样。

解决方案 2 的插图


样本

import java.util.ArrayList;
import java.util.List;

public class ChainModeCallbackHandler implements ImageDownloader.ImageDownloadCallback {

    // List of args to start the task. Use pojo classes if your task has multiple args
    private static List<String> urls;

    // List of results
    private static List<String> results;

    // Optional.
    private static ImageDownloader.ProgressUpdateListener progressUpdateListener;

    // Leave it as it is
    private int index;

    public static void start(List<String> urls, ImageDownloader.ProgressUpdateListener listener) {
        ChainModeCallbackHandler.urls = urls;
        results = new ArrayList<>();
        progressUpdateListener = listener;

        //Start with the first task
        ImageDownloader.getInstance(listener).download(urls.get(0), new ChainModeCallbackHandler(0));
    }

    private ChainModeCallbackHandler(int index) {
        this.index = index;
    }

    @Override
    public void onSuccess(String imagePath) {
        results.add(imagePath);
        afterCallback();
    }

    @Override
    public void onFailure() {
        afterCallback();
    }

    private void afterCallback() {
        int nextIndex = index+1;
        if (nextIndex<urls.size()) {
            //Tasks are not completed yet. Do next task
            ImageDownloader.getInstance(progressUpdateListener).download(urls.get(nextIndex),
                    new ChainModeCallbackHandler(nextIndex));
        } else {
            // All tasks completed. List "results" now holds the result

            DemoActivity.isTasksInProgress = false;
        }
    }
}




##方法3: 并行执行任务。

解决方案 3 的插图

并行
执行有时有助于节省时间


样本

import java.util.ArrayList;
import java.util.List;

public class ParallelModeCallbackHandler {

    // List of args to start the task. Use pojo classes if your task has multiple args
    private static List<String> urls;

    // List of results
    private static List<String> results;

    // Leave it as it is
    private static int count;

    public static void start(List<String> urls, ImageDownloader.ProgressUpdateListener listener) {
        ParallelModeCallbackHandler.urls = urls;
        results = new ArrayList<>();
        count = 0;

        // Start all tasks
        for (String url :
                urls) {
            //Replace with your task and its callback
            ImageDownloader.getInstance(listener).download(url, new ImageDownloader.ImageDownloadCallback() {
                @Override
                public void onSuccess(String imagePath) {
                    results.add(imagePath);
                    afterCallback();
                }

                @Override
                public void onFailure() {
                    afterCallback();
                }
            });
        }
    }

    private ParallelModeCallbackHandler() {}

    private static void afterCallback() {
        if (++count==urls.size()) {
            // All tasks completed. List "results" now holds the result

            DemoActivity.isTasksInProgress = false;
        }
    }
}
于 2019-02-16T19:00:16.603 回答
2

您可以在正在执行的命令上调用 wait()。

尽管在执行此操作之前,您应该关闭您调用的命令的处理程序等待。您可以通过将 RootTools.handlerEnabled 设置为 false 或在每个单独的命令中使用构造函数并传入 false 来禁用该命令的处理程序来对每个命令执行此操作。

这很重要,因为如果使用了处理程序,那么它将尝试在您调用 wait() 的线程上调用回调方法,这将导致死锁。

当您关闭命令的处理程序并调用 wait() 时,命令将在完成时调用 notifyAll() 以便您的线程将恢复。

不利的一面是回调方法将不再在您正在处理的线程中完成,因此您将无法通过这些回调方法执行任何 UI 工作,除非您实现处理程序或其他可接受的解决方案来处理有了这个。

于 2013-07-03T02:44:24.447 回答
0

使用CountDownLatch,我将在此处复制它们的用法示例以更好地突出显示语法(:

class Driver { // ...
  void main() throws InterruptedException {
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i) // create and start threads
      new Thread(new Worker(startSignal, doneSignal)).start();

    doSomethingElse();            // don't let run yet
    startSignal.countDown();      // let all threads proceed
    doSomethingElse();
    doneSignal.await();           // wait for all to finish
  }
}

class Worker implements Runnable {
  private final CountDownLatch startSignal;
  private final CountDownLatch doneSignal;
  Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
    this.startSignal = startSignal;
    this.doneSignal = doneSignal;
  }
  public void run() {
    try {
      startSignal.await();
      doWork();
      doneSignal.countDown();
    } catch (InterruptedException ex) {} // return;
  }

  void doWork() { ... }
}
于 2013-07-02T06:25:00.267 回答