如何获取Task的void类型onComplete()方法的执行结果?
如何正确获取异步Task的执行结果
首先咱们先理清你之前踩坑的根源:
- 直接调用
mytask.wait()抛出IllegalMonitorStateException,是因为**wait()要求当前线程必须先持有该对象的锁**,你得先通过synchronized(mytask)获取锁才能调用,但更关键的是,如果这个库的Task内部没有在完成时调用notify()/notifyAll(),就算加了锁也会一直阻塞,所以这种方法根本不靠谱。 - 用
while(!mytask.isComplete()){}的忙等方式,会让你的线程一直占用CPU循环检查状态,要是在UI线程里这么干,直接就会导致界面完全冻结——因为UI线程没时间处理用户交互和界面绘制了。
接下来给你两种靠谱的解决方案,按需选择:
方案1:遵循库的异步回调设计(推荐)
这个库的Task本身就是为异步场景设计的——用onComplete()和onFail()回调来处理结果,所以最合理的方式是把你需要用结果做的逻辑直接放到回调里,而不是强行同步返回:
// 假设你的结果类型是ResultType mytask.onComplete(() -> { // 在这里处理任务成功的结果 ResultType result = getTaskResult(); // 替换成你获取结果的实际逻辑 updateUI(result); // 比如更新界面 doSomethingElseWithResult(result); // 或者调用其他业务方法 }); mytask.onFail(() -> { // 处理任务失败的情况 showErrorToast("任务执行失败"); handleTaskFailure(); });
这种方式完全符合库的设计初衷,不会有线程阻塞或冻结的问题,也是异步编程的常规做法。
方案2:用同步工具类强制同步获取结果(仅适用于非UI线程)
如果你确实需要在某个同步方法里拿到结果才能继续执行(比如后台线程的业务逻辑),可以用CountDownLatch来实现安全的等待,它不会像忙等那样浪费CPU:
import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; // 初始化一个计数为1的CountDownLatch CountDownLatch latch = new CountDownLatch(1); // 用AtomicReference来线程安全地保存结果和异常 AtomicReference<ResultType> taskResult = new AtomicReference<>(); AtomicReference<Exception> taskError = new AtomicReference<>(); mytask.onComplete(() -> { taskResult.set(getTaskResult()); // 保存成功结果 latch.countDown(); // 计数减1,通知等待的线程 }); mytask.onFail(() -> { taskError.set(new RuntimeException("任务执行失败")); // 保存失败信息 latch.countDown(); // 同样通知等待的线程 }); try { // 等待任务完成,最多等待10秒(可根据需求调整超时时间) if (latch.await(10, TimeUnit.SECONDS)) { if (taskError.get() != null) { // 任务失败,抛出异常或处理错误 throw taskError.get(); } // 任务成功,返回结果 return taskResult.get(); } else { // 超时处理 throw new TimeoutException("任务执行超时"); } } catch (InterruptedException e) { // 等待被中断,恢复线程中断状态并抛出异常 Thread.currentThread().interrupt(); throw new RuntimeException("等待任务完成时被中断", e); }
为什么这个方法可行?
CountDownLatch会让等待的线程进入休眠状态(不占用CPU),直到countDown()被调用(任务完成或失败时),线程才会被唤醒继续执行。用AtomicReference是因为在回调方法里给外部变量赋值需要保证线程安全,避免并发问题。
最后再提醒一句:如果是在UI线程(比如Android主线程、Swing的EDT),绝对不要用方案2,否则会导致界面冻结,用户体验极差,甚至触发ANR(应用无响应)。
内容的提问来源于stack exchange,提问作者AndreaF
相关产品推荐
相关产品推荐

