JUC并发编程包Future模式详解

Future模式是多线程开发中常见的设计模式,它的核心思想是异步调用。对于Future模式来说,它无法立即返回你需要的数据,但是它会返回一个契约,将来你可以凭借这个契约去获取你需要的信息。

应用场景

两个任务没有必然的前后关系,如果在一个线程中串行执行,就有些浪费时间,不如让两个线程去并行执行这两个任务,执行完了到主线程去汇报就可以了。(让任务后台运行,不阻塞线程),具体应用场景和好处我们在上一篇讲解CountDownLatch的使用场景时也有说明。JUC并发编程包CountDownLatch详解https://blog.csdn.net/weixin_37854829/article/details/122604813

实现思路

其实JDK经准备好了一套完美的实现方式。我们用于执行的异步线程只需要实现Callable接口,相当于Runnable,Callable接口中有一个方法call(),相当于run(),在里面写自己的业务。把这个线程放在FutureTask中,这个相当于凭证。把FutureTask交给线程池来进行异步处理,我们可以通过FutureTask中的get()方法返回结果,结果没有返回会阻塞。

1、Callable+FutureTask 获取异步线程结果

package com.example.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author youyun.xu
 * @Description: FutureTask获取异步线程返回值
 * @date 2022/1/23 17:18
 */
public class Demo1 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        //1、启动异步线程执行任务
        //异步线程1(耗时3秒)
        Callable<Worker> worker1 = new Callable<Worker>() {
            @Override
            public Worker call() throws Exception {
                Thread.sleep(1000 * 3);
                Worker worker = new Worker();
                worker.setName("马云");
                worker.setPhone("13000000001");
                return worker;
            }
        };
        FutureTask futureTask1 = new FutureTask<>(worker1);
        new Thread(futureTask1).start();

        //异步线程2(耗时4秒)
        Callable<Worker> worker2 = new Callable<Worker>() {
            @Override
            public Worker call() throws Exception {
                Thread.sleep(1000 * 4);
                Worker worker = new Worker();
                worker.setName("马化腾");
                worker.setPhone("13000000002");
                return worker;
            }
        };
        FutureTask futureTask2 = new FutureTask<>(worker2);
        new Thread(futureTask2).start();

        //2、模拟主线程处任务
        Thread.sleep(1000 * 2);
        System.out.println("此处主线程阻塞2秒,模拟处理其他任务");


        //3、汇总其他2个异步线程处理结果并打印
        List<Worker> list = new ArrayList<>();
        list.add((Worker) futureTask1.get());
        list.add((Worker) futureTask2.get());
        list.parallelStream().forEach(worker -> {
            System.out.println("打印结果-> " + worker.toString());
        });
    }

    static class Worker {
        private String name;
        private String phone;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", phone='" + phone + '\'' +
                    '}';
        }
    }

}

2、Callable+Future+CountDownLatch+ExecutorService 获取异步线程结果

结合上面的例子,我们再进行改进,结合Callable+Future+CountDownLatch+ExecutorService一起使用,这样用法才更贴近我们真实使用场景。

package com.example.demo;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

/**
 * @author youyun.xu
 * @Description:
 * @date 2022/1/23 19:02
 */
public class Demo2 {
    /**
     * 初始可以缓存线程的线程池,官方提供的线程池没有边界,建议按照需要设置线程值大小,此处仅为演示
     */
    final static ExecutorService executorService = Executors.newCachedThreadPool();

    static class WorkerHandle implements Callable<Worker> {

        private CountDownLatch countDownLatch;
        private Worker worker;

        public WorkerHandle(CountDownLatch countDownLatch, Worker worker) {
            this.countDownLatch = countDownLatch;
            this.worker = worker;
        }

        @Override
        public Worker call() throws Exception {
            Thread.sleep(1000 * 4);
            try {
                worker.setAge(new Random().nextInt(10));
            } finally {
                countDownLatch.countDown();
            }
            return worker;
        }
    }


    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CountDownLatch countDownLatch = new CountDownLatch(2);

        //线程池依次提交有返回值的异步任务
        Future<Worker> worker1 = executorService.submit(new WorkerHandle(countDownLatch, new Worker("马云", "13000000001")));
        Future<Worker> worker2 = executorService.submit(new WorkerHandle(countDownLatch, new Worker("马化腾", "13000000002")));

        //等待子线程结束
        countDownLatch.await();

        //获取异步子线程结果
        List<Worker> workers = new ArrayList<>();
        workers.add(worker1.get());
        workers.add(worker2.get());

        workers.parallelStream().forEach(worker -> {
            System.out.println("打印结果-> " + worker.toString());
        });
    }

    static class Worker {
        private String name;
        private String phone;
        private Integer age;

        public Worker(String name, String phone) {
            this.name = name;
            this.phone = phone;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getPhone() {
            return phone;
        }

        public void setPhone(String phone) {
            this.phone = phone;
        }

        @Override
        public String toString() {
            return "Worker{" +
                    "name='" + name + '\'' +
                    ", phone='" + phone + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

上面例子结合了线程池,闭锁、Future模式等多线程开发中常用的知识,这里为了更好理解,所以代码没有再做抽象,实际编码环境中我们还可以结合设计模式,比如解决get\set我们可以考虑建造者模式,让代码更加优雅。此处仅作演示,只为通俗易懂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热情的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值