多线程处理返回值

本文探讨了软件开发中技术分享的重要性,并深入讲解了线程的实现方式,特别是使用Callable接口来获取线程执行后的结果,通过实例展示了如何利用线程池执行多个线程任务并收集结果。

好久没写博客,看了下上一篇发表的时间发现已是一年多前,看到消息处的小红点,点了下发现仍然有很多新朋友关注了我,给我留言,给我的博文点赞,让我发现技术分享的重要性,不仅仅是影响一个人,而是影响一群人。软件开发是一份很潮的工作,会随着历史的潮头不断奔流不息,只有屹立潮头的人才不会被淘汰。逆水行舟,不进则退,多少比你差的人比你还几倍的努力。如果你总是止步不前,拒绝新的技术,你还期望公司给你加薪,你还指望同事教你该怎么写代码还拿着比同事高的工资,即使你现在还能苟延残喘,但终究会被抛弃。

说了很多废话,只是希望自己警醒,希望认同上述观点的人同样能感受到那些每天看凌晨三点太阳的人的压力,其他不认同的人大概可以跳过直接看下面的内容。本文要写的内容是关于线程,工作中有些写业务代码的开发这块用得比较少,但是面试时面试官总喜欢问多线程相关问题,例如下面这些:

1)什么是线程

2)怎么样实现多线程

3)有没有用过线程池

4)怎么样实现一个线程池

5)不要线程池可不可以

……

这些问题大家可以思考下,本文不对上述问题进行解答。大家都知道new一个线程或者实现Ruannable接口,调度这些线程是没有返回值的,如果我们需要线程任务处理完毕会返回一个成功与否的标志,那么可以采用下面的方式

public class Task implements Callable<String> {
    private int id;

    public Task(int id) {
        this.id = id;
    }

    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    @Override
    public String call() throws Exception {
        return "线程号:" + Thread.currentThread().getId();
    }
}

线程类没有实现Runnable接口,而是实现Callable接口的call方法。

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        ArrayList<Future<String>> list = new ArrayList<Future<String>>();
        for (int i =0;i<10;i++){
            list.add(executorService.submit(new Task(i)));
        }

        for(Future<String> future : list){
            System.out.println(future.get());
        }
    }
}

创建了一个缓存线程池(线程池的线程数会动态伸缩),将线程任务放到线程池中执行,结果被保存到Future泛型的List中。最后打印出所有线程任务的结果。

大家可以思考下线程池执行任务返回结果是同步还是异步的,这种同步或者异步的效果又是通过什么方式实现的,我们拿到的结果是否真实。

<think> 我们参考了多个引用,其中提到了几种获取多线程返回值的方法: 引用[1]和[4]提到使用实现Callable接口的方式,通过Future来获取返回值。 引用[2]提到通过扩展Thread类,并设置一个成员变量来存储返回值,然后通过轮询该变量来获取返回值(但这种方法不精确)。 引用[3]提到了使用线程池(ThreadPoolExecutor)和CountDownLatch来获取返回值。 引用[5]则展示了使用Callable和FutureTask的具体例子。 综合来看,常用的获取多线程返回值的方法有: 1. 实现Callable接口,配合Future或FutureTask来获取返回值(推荐)。 2. 通过自定义线程类,设置一个成员变量存储返回值,然后通过轮询或等待的方式获取(不推荐,因为效率低且不精确)。 3. 使用线程池提交Callable任务,获取Future对象,进而获取返回值(推荐)。 我们将重点介绍推荐的方法:使用Callable和Future(FutureTask)以及使用线程池(ExecutorService)来获取返回值。 ### 方法一:使用Callable和FutureTask 步骤: 1. 创建一个实现Callable接口的类,并实现call方法,在call方法中编写任务逻辑并返回结果。 2. 创建该Callable实现类的实例。 3. 使用FutureTask来包装Callable对象。 4. 将FutureTask对象作为Thread的target启动线程。 5. 调用FutureTask的get()方法获取返回值(此方法会阻塞直到任务完成)。 示例代码: ```java import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.concurrent.ExecutionException; public class CallableExample { public static void main(String[] args) { Callable<Integer> callable = new MyCallableTask(); FutureTask<Integer> futureTask = new FutureTask<>(callable); Thread thread = new Thread(futureTask); thread.start(); try { // 获取返回值,会阻塞直到任务完成 Integer result = futureTask.get(); System.out.println("任务执行结果: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } } class MyCallableTask implements Callable<Integer> { @Override public Integer call() throws Exception { // 模拟耗时操作 Thread.sleep(1000); int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } return sum; } } ``` ### 方法二:使用线程池(ExecutorService)提交Callable任务 步骤: 1. 创建线程池(如通过Executors工具类或直接使用ThreadPoolExecutor)。 2. 提交Callable任务给线程池,得到Future对象。 3. 调用Future对象的get()方法获取返回值。 示例代码: ```java import java.util.concurrent.*; public class ExecutorServiceExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); Callable<Integer> callable = new MyCallableTask(); // 同上例中的MyCallableTask Future<Integer> future = executor.submit(callable); try { Integer result = future.get(); System.out.println("任务执行结果: " + result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } finally { executor.shutdown(); } } } ``` ### 方法三:使用CountDownLatch等待多个线程完成(引用[3]中提及) 当需要等待多个线程都完成后再获取所有返回值时,可以使用CountDownLatch。但注意,CountDownLatch本身并不直接传递返回值,而是用于同步,我们可以结合Future或自定义的返回值存储来使用。 示例(结合线程池和CountDownLatch): ```java import java.util.concurrent.*; import java.util.*; public class MultiThreadExample { public static void main(String[] args) throws InterruptedException, ExecutionException { int taskCount = 5; ExecutorService executor = Executors.newFixedThreadPool(taskCount); CountDownLatch latch = new CountDownLatch(taskCount); List<Future<Integer>> futures = new ArrayList<>(); for (int i = 0; i < taskCount; i++) { Callable<Integer> task = new MyCallableTask(latch); // 修改MyCallableTask以接收latch并在完成后countDown Future<Integer> future = executor.submit(task); futures.add(future); } // 等待所有任务完成 latch.await(); // 获取每个任务的结果 for (Future<Integer> future : futures) { Integer result = future.get(); // 注意这里不会再阻塞,因为任务已经完成 System.out.println("任务结果: " + result); } executor.shutdown(); } } class MyCallableTask implements Callable<Integer> { private final CountDownLatch latch; public MyCallableTask(CountDownLatch latch) { this.latch = latch; } @Override public Integer call() throws Exception { try { // 模拟耗时操作 Thread.sleep(1000); int sum = 0; for (int i = 1; i <= 100; i++) { sum += i; } return sum; } finally { latch.countDown(); // 确保无论是否异常都会减少计数 } } } ``` ### 总结 1. 单个线程任务获取返回值:推荐使用Callable和FutureTask,或者使用线程池的submit方法得到Future对象。 2. 多个线程任务获取返回值:可以使用线程池提交多个Callable任务,得到一组Future对象,然后逐个获取。如果需要等待所有任务完成后再统一获取,可以结合CountDownLatch(如方法三),但注意Future.get()本身就会阻塞直到任务完成,所以如果不需要在任务完成后立即做其他操作,也可以直接遍历Future列表调用get()。 注意事项: - Future.get()方法是阻塞的,如果任务没有完成,会一直等待。可以设置超时时间,使用get(long timeout, TimeUnit unit)。 - 使用线程池后,记得关闭线程池(shutdown())。 §§相关问题§§ 1. Java中Callable和Runnable接口有什么区别? 2. 如何避免Future.get()方法造成的阻塞? 3. Java线程池有哪些常用类型,如何选择? 4. CountDownLatch和CyclicBarrier有什么异同? </think> ### Java获取多线程返回值的方法 在Java中获取多线程返回值主要有以下几种方式,根据不同的使用场景推荐不同的实现方案: #### 1. **实现Callable接口 + Future(推荐方案)** [^1][^4][^5] ```java import java.util.concurrent.*; public class CallableExample { public static void main(String[] args) throws Exception { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<String> future = executor.submit(new MyCallable()); String result = future.get(); // 阻塞获取返回值 System.out.println("返回值: " + result); executor.shutdown(); } } class MyCallable implements Callable<String> { @Override public String call() throws Exception { Thread.sleep(2000); return "任务完成"; } } ``` - **优点**:线程池管理、精准阻塞等待结果 - **关键类**: - `Callable<V>`:带返回值的线程任务接口 - `Future<V>`:获取异步计算结果 - **注意事项**: - `future.get()`会阻塞当前线程直到任务完成 - 可通过`future.get(timeout, unit)`设置超时时间 #### 2. **扩展Thread类 + 成员变量** [^2] ```java public class ValueThread extends Thread { private String result; // 存储返回值的成员变量 @Override public void run() { result = "计算结果"; } public String getResult() { return result; } } // 使用示例 public static void main(String[] args) throws InterruptedException { ValueThread thread = new ValueThread(); thread.start(); while(thread.getResult() == null) { Thread.sleep(100); // 轮询等待 } System.out.println(thread.getResult()); } ``` - **缺点**:需要手动轮询检查结果,效率较低 - **适用场景**:简单场景下的快速实现 #### 3. **线程池 + Future + CountDownLatch(批量任务)** [^3][^5] ```java public class BatchTaskExample { public static void main(String[] args) throws Exception { int taskCount = 5; ExecutorService pool = Executors.newFixedThreadPool(taskCount); CountDownLatch latch = new CountDownLatch(taskCount); List<Future<Integer>> futures = new ArrayList<>(); for (int i = 0; i < taskCount; i++) { futures.add(pool.submit(new ComputeTask(latch, i))); } latch.await(); // 等待所有任务完成 for (Future<Integer> f : futures) { System.out.println("结果: " + f.get()); } pool.shutdown(); } } class ComputeTask implements Callable<Integer> { private final CountDownLatch latch; private final int id; public ComputeTask(CountDownLatch latch, int id) { this.latch = latch; this.id = id; } @Override public Integer call() { try { return id * 10; // 模拟计算 } finally { latch.countDown(); // 任务完成计数器减1 } } } ``` - **优势**:适合批量异步任务处理 - **核心组件**: - `ThreadPoolExecutor`:管理线程池 - `CountDownLatch`:同步计数器,控制任务完成状态 #### 4. **CompletableFuture(Java 8+ 高级方案)** ```java CompletableFuture.supplyAsync(() -> { // 模拟耗时任务 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } return "结果数据"; }).thenAccept(result -> { System.out.println("异步处理结果: " + result); }); ``` - **特点**:非阻塞回调、链式调用 - **优势**:支持异常处理、结果组合等高级功能 ### 方案对比 | 方法 | 优点 | 缺点 | 适用场景 | |---------------------|-------------------------------|--------------------------|-----------------------| | Callable+Future | 官方标准方案,线程池管理 | 需要处理阻塞 | 单任务获取结果 | | Thread成员变量 | 实现简单 | 需手动轮询,效率低 | 简单测试场景 | | 线程池+CountDownLatch| 高效处理批量任务 | 代码稍复杂 | 批量并行任务处理 | | CompletableFuture | 非阻塞,函数式编程 | 需要Java 8+支持 | 复杂异步流处理 | ### 最佳实践建议 1. 优先使用`Callable`+`ExecutorService`的组合方案[^1][^5] 2. 批量任务处理时配合`CountDownLatch`确保所有任务完成[^3] 3. Java 8+项目推荐使用`CompletableFuture`实现响应式编程 4. 避免直接继承`Thread`类的方案,不利于资源管理[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值