主线程中获取异步请求的结果

在 Java 中,要在主线程中获取异步请求的结果,可以使用多种方式。以下是几种常见的实现方法:

使用Future和ExecutorService

这是 Java 并发包中提供的标准方式,通过Future来获取异步任务的结果。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class MainThreadAsync {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        try {
            // 提交异步任务
            Future<String> future = executor.submit(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    // 模拟异步请求(比如网络请求)
                    Thread.sleep(2000); // 模拟耗时操作
                    System.out.println("异步任务执行完毕");
                    return "异步请求返回的数据";
                }
            });
            
            // 主线程可以做其他事情
            System.out.println("主线程正在处理其他任务...");
            
            // 阻塞等待异步结果(get()方法会阻塞直到结果返回)
            String result = future.get();
            System.out.println("获取到异步结果: " + result);
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executor.shutdown();
        }
    }
}

结果

主线程正在处理其他任务...
异步任务执行完毕
获取到异步结果: 异步请求返回的数据

使用CompletableFuture(Java 8 + 推荐)

CompletableFuture提供了更灵活的异步编程方式,支持链式调用和非阻塞回调。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureExample {
    public static void main(String[] args) {
        // 发起异步请求
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                // 模拟异步请求
                Thread.sleep(2000);
                System.out.println("异步请求完成");
                return "异步请求返回的数据";
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });
        
        // 主线程可以做其他事情
        System.out.println("主线程正在处理其他任务...");
        
        try {
            // 阻塞获取结果
            String result = future.get();
            System.out.println("获取到异步结果: " + result);
            
            // 或者使用非阻塞的回调方式
            future.thenAccept(result1 -> {
                System.out.println("回调方式获取到异步结果: " + result1);
            });
            // 等待异步任务完成 这里必须等待,否则主线程执行完毕,程序结束,但是异步线程没有执行完成。
            future.join();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
主线程正在处理其他任务...
异步请求完成
获取到异步结果: 异步请求返回的数据
回调方式获取到异步结果: 异步请求返回的数据

使用回调接口(传统方式)

自定义回调接口,当异步任务完成时主动通知主线程。

import java.util.concurrent.CountDownLatch;

// 定义回调接口
interface Callback {
    void onSuccess(String result);
    void onFailure(Exception e);
}

public class AsyncResultInMainThread {
    // 用于存储异步结果
    private static String asyncResult;
    // 用于存储可能发生的异常
    private static Exception asyncException;

    public static void main(String[] args) {
        // 创建CountDownLatch,用于主线程等待异步结果
        CountDownLatch latch = new CountDownLatch(1);
        
        System.out.println("主线程:发起异步请求");
        
        // 发起异步请求
        asyncRequest(new Callback() {
            @Override
            public void onSuccess(String result) {
                // 这个方法在异步线程中执行,仅用于存储结果
                asyncResult = result;
                latch.countDown(); // 通知主线程结果已准备好
            }
            
            @Override
            public void onFailure(Exception e) {
                // 存储异常信息
                asyncException = e;
                latch.countDown(); // 通知主线程发生异常
            }
        });
        
        // 主线程可以先处理其他不需要异步结果的任务
        System.out.println("主线程:处理其他任务...");
        
        try {
            // 主线程等待异步结果,最多等待5秒
            latch.await();
            
            // 以下代码在主线程中执行
            if (asyncException != null) {
                System.out.println("主线程:异步请求发生错误");
                asyncException.printStackTrace();
            } else {
                System.out.println("主线程:获取到异步结果 - " + asyncResult);
                // 在这里可以安全地在主线程中使用异步结果
                processResultInMainThread(asyncResult);
            }
        } catch (InterruptedException e) {
            System.out.println("主线程:等待被中断");
            Thread.currentThread().interrupt();
        }
        
        System.out.println("主线程:任务完成");
    }
    
    // 在主线程中处理结果的方法
    private static void processResultInMainThread(String result) {
        System.out.println("主线程处理结果:" + result.toUpperCase());
    }
    
    // 模拟异步请求方法
    private static void asyncRequest(Callback callback) {
        new Thread(() -> {
            try {
                System.out.println("异步线程:执行网络请求...");
                // 模拟网络请求耗时
                Thread.sleep(2000);
                
                // 模拟返回结果
                String result = "来自服务器的响应数据";
                System.out.println("异步线程:请求完成,准备通知主线程");
                
                // 调用回调,通知结果(此时仍在异步线程中)
                callback.onSuccess(result);
            } catch (InterruptedException e) {
                callback.onFailure(new Exception("请求被中断", e));
            }
        }).start();
    }
}

主线程:发起异步请求
主线程:处理其他任务...
异步线程:执行网络请求...
异步线程:请求完成,准备通知主线程
主线程:获取到异步结果 - 来自服务器的响应数据
主线程处理结果:来自服务器的响应数据
主线程:任务完成

第三种方式是否线程安全

Java 内存模型(JMM)中的可见性问题。
问题:Java 中,每个线程可能会将共享变量(如asyncResult和asyncException)的副本保存到自己的工作内存中,而不是直接操作主内存中的变量:
异步线程更新了asyncResult的值,但可能只更新了自己工作内存中的副本,尚未同步到主内存。
主线程可能仍在读取自己工作内存中的旧值,导致无法看到最新结果。

在前面的代码中,CountDownLatch的countDown()和await()方法会建立线程间的 happens-before 关系,这保证了:
异步线程在countDown()之前的所有操作(包括对asyncResult的赋值)
一定对执行await()之后的主线程可见
这是因为CountDownLatch内部使用了同步机制(如AbstractQueuedSynchronizer),会触发内存屏障(Memory Barrier),强制线程将工作内存中的数据刷新到主内存,或从主内存重新加载数据。

选择建议:

如果使用 Java 8 及以上版本,推荐使用CompletableFuture,它功能强大且代码简洁
Future方式适合简单场景,但功能相对有限
回调接口方式更适合传统项目或需要自定义通知机制的场景

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值