多线程Future设计模式+异步处理

本文介绍了如何在Java中实现异步发送邮件功能,以提高用户体验。通过线程和FutureData类,模拟thread-per-message模式,确保在处理订单后立即返回结果,同时异步发送邮件。进一步讨论了使用JUC(Java并发工具包)的FutureTask和CompletableFuture优化方案,以及它们如何简化代码和增强异步操作的感知。
摘要由CSDN通过智能技术生成

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

现实场景

想实现异步发送邮件的功能,比如说,在客户成功提交了一个订单时,程序需要发送邮件给管理员和客户,然后报告说订单提交成功
如果不用异步方式,那么当邮件发送很耗时的时候,用户就要等很久才能看到成功提示,
因此我想,在保存了订单数据后,就用另一个线程来发送邮件,从而当前线程立即就可以向用户反馈订单提交成功的提示信息。
在这里插入图片描述
这个就像thread-per-message模式
只不过有了futuredata可以用来存储结果 仍然是主线程立即结束返回响应 新启线程异步做委托的任务 主线程利用空闲时间去做其他事 多线程资源利用率高

当需要使用的时候向futuredata取值

基础代码实现

在这里插入图片描述

host类 和之前模式一样 只不过多了一个返回结果

package Future;

public class Host {

    public Data request(final char c,final int times) {
        System.out.println(Thread.currentThread().getName() + "输入手机号--"+System.currentTimeMillis());

        final MyFutureData myFutureData = new MyFutureData();
        /**
         * 异步新启动一个线程 后台操作 host主线程不再等待 直接返回操作成功提示信息
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "异步执行业务 BEGIN--"+System.currentTimeMillis());
                RealData realData = new RealData(c, times);
                myFutureData.setContent(realData);
                System.out.println(Thread.currentThread().getName() + "异步执行结束 END--"+System.currentTimeMillis());


            }
        }).start();
        System.out.println(Thread.currentThread().getName() + "操作成功--"+System.currentTimeMillis());

        return myFutureData;
    }

}

package Future;

/**
 * @author sqpstart
 * @create 2021-04-28 13:23
 */
public interface Data {
    public String  getContent();
}

future类 守护类 里面维护真正的realdata

package Future;

/**
 * @author sqpstart
 * @create 2021-04-28 13:23
 */
public class MyFutureData implements  Data {
    private   RealData realData;
    private volatile boolean read; //已经存过值了
    @Override
    public synchronized String getContent() {
      while(!read){
          try {
              System.out.println("业务还未进行完毕"+Thread.currentThread().getName()+"等待");
              wait();
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
      return realData.getContent();
    }


    public synchronized void setContent(RealData realData) {
       if(read){
           return;
       }
        this.realData=realData;
        read=true;
        notifyAll();

    }
}

realdata类 构造函数就是业务逻辑处理的本身 生成该对象 调用构造函数 就已经赋值future结果了

package Future;

/**
 * @author sqpstart
 * @create 2021-04-28 13:23
 */
public class RealData implements  Data{

    private String content;
   //将异步的业务结果 在realdata的构造函数里就封装好了
    public RealData(char c, int times) {
        System.out.println(Thread.currentThread().getName()+"进行真正的业务处理并返回数据"+System.currentTimeMillis());
        char[] buffer=new char[times];
        for (int i = 0; i < times; i++) {
           buffer[i]=c;
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        content=new String(buffer);
    }

    @Override
    public String getContent() {
        System.out.println(Thread.currentThread().getName()+"来获取内容了");
        return content;
    }
}

package Future;

/**
 * @author sqpstart
 * @create 2021-04-28 13:45
 */
public class Client {
    public static void main(String[] args) {
        Host host = new Host();
        Data a = host.request('A', 10);
        Data b = host.request('B', 15);
        Data c = host.request('C', 7);

        System.out.println("我们都收到了请求操作成功的 回复,资源利用主线程去做其他事了"+System.currentTimeMillis());

        try {
            Thread.sleep(2500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程回来取数据了"+System.currentTimeMillis());
        System.out.println(a.getContent()+","+b.getContent()+","+c.getContent());


    }
}

JUC代码实现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
以前host显示的将realdata set进future里 现在这里是call()方法返回值 会放到futuretask的set里

在这里插入图片描述

public class HostJUC {
    public FutureDataJUC request(final int count, final char c) {
        //向构造函数传递callable接口 并制定泛型 声明业务
        FutureDataJUC futureDataJUC = new FutureDataJUC(new Callable<RealData>() {
            @Override
            public RealData call() throws Exception {
                return new RealData(c, count);//只是生成对象 还没有执行业务
                //等下面线程调用起来才执行
            }
        });
        //线程启动
        new Thread(futureDataJUC).start();
        return futureDataJUC;
    }
}

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

futureData继承futureTask< T> 构造函数中参数为callable
HOST在调用Futuredata完成setRealData的工作已经由父类futureTask 将callable的返回结果set来代替了
futuredata只需提供给客户端一个getCOntent的功能 并且其中也不再维护私有的realdata对象和守护模式等多线程操作 而是使用futuretask提供的get()获取Callable返回的realdata进行返回
callable里面维护了真正的核心业务 并且对之前拿一长串new Thread(){…}做了封装

在这里插入图片描述
在这里插入图片描述
其实也可以直接使用futuretask 操作 上面的示例 用futureData继承futureTask做了一个封装 其实可以省掉这一层封装 直接在HOST这个操作类调用task

futureData 唯一不同就是封装了getContent 函数 而如果直接用futureTask在客户端直接做了

public class Client {
    public static void main(String[] args) {
        //Host host = new Host();
        HostJUC host=new HostJUC();
       /* Data a = host.request('A', 10);
        Data b = host.request('B', 15);
        Data c = host.request('C', 7);*/

        FutureTask<RealData> a = host.request('A', 10);
        FutureTask<RealData> b = host.request('B', 15);
        FutureTask<RealData> c = host.request('C', 7);

        System.out.println("我们都收到了请求操作成功的 回复,资源利用主线程去做其他事了"+System.currentTimeMillis());

        try {
            Thread.sleep(2500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程回来取数据了"+System.currentTimeMillis());
       // System.out.println(a.getContent()+","+b.getContent()+","+c.getContent());

        try {
            System.out.println(a.get().getContent()+","+b.get().getContent()+","+c.get().getContent());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }


    }
package Future;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * @author sqpstart
 * @create 2021-04-28 15:39
 */
public class HostJUC {
 /*   *//*   public futureTaskrequest(final int count, final char c) {
           //向构造函数传递callable接口 并制定泛型 声明业务
           futureTaskfutureTask= new FutureDataJUC(
            new Callable<RealData>() {
               @Override
               public RealData call() throws Exception {
                   return new RealData(c, count);//并没有生成对象
                   //等下面线程调用起来才声明
               }
           });
           //线程启动
           new Thread(futureDataJUC).start();
           return futureDataJUC;
       }*/
    public FutureTask<RealData> request( final char c,final int count) {
        //向构造函数传递callable接口 并制定泛型 声明业务
        FutureTask futureTask = new FutureTask(
                new Callable<RealData>() {
                    @Override
                    public RealData call() throws Exception {
                        return new RealData(c, count);//并没有生成对象
                        //等下面线程调用起来才声明
                    }
                });
        //线程启动
        new Thread(futureTask).start();
        return futureTask;
    }
}

Completable

package Future;

import java.util.concurrent.CompletableFuture;

/**
 * @author sqpstart
 * @create 2021-04-28 22:11
 */
public class MyCompletableFuture {
    public static void main(String[] args) throws InterruptedException {
        CompletableFutureHost host=new CompletableFutureHost();
        CompletableFuture<String> a = host.request('A', 10);
        CompletableFuture<String> b = host.request('B', 15);
        CompletableFuture<String> c = host.request('C', 7);

        Thread.sleep(1000);

        System.out.println(a.join()+b.join()+c.join());

    }
}
class CompletableFutureHost{

  public CompletableFuture request(final char c, final int count){
      CompletableFuture<String> async = CompletableFuture.supplyAsync(() -> {

          return new RealData(c, count).getContent();
      });
    return async;
  }
}

在这里插入图片描述
在这里插入图片描述
感知异步的结果以及异步操作是否发生异常
在这里插入图片描述

public static  void huidiao(){
        System.out.println(Thread.currentThread().getName()+"  开始         "+System.currentTimeMillis());

        CompletableFuture<Integer> future=CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName()+"    "+System.currentTimeMillis());
            int i=10/2;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return i ;
        }).whenComplete((t,u)->{
            System.out.println("异步执行结束,回调结果和异常");
            System.out.println(Thread.currentThread().getName()+"    "+System.currentTimeMillis());
            System.out.println(t+"   "+u);
        });

        System.out.println(Thread.currentThread().getName()+future.join());
        System.out.println(Thread.currentThread().getName()+"  结束     "+System.currentTimeMillis());

    }

在这里插入图片描述
在这里插入图片描述
对异步结果和操作异常的处理
在这里插入图片描述
在这里插入图片描述
注意返回的泛型 thenApply会有返回结果 所以泛型是U
其余的没有返回结果 泛型是void

多线程异步实战

在这里插入图片描述

在这里插入图片描述
上面这个类会自动注入容器 然后找到下图配置文件中与gulimall.thread为前缀的配置
在这里插入图片描述
直接在线程池配置类中引入刚才的properties 并且把自己配置的线程池@BEAN 注入到容器里 搭配completablefuture就用了

因为线程池不会根据业务结束就销毁线程 而是一直让线程活着 节省销毁的时间
而且新任务来了 也不用等待 创建线程去应对 而是直接执行 节省时间 资源

在这里插入图片描述

自动注入 我们给容器中放入的线程池
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值