现实场景
想实现异步发送邮件的功能,比如说,在客户成功提交了一个订单时,程序需要发送邮件给管理员和客户,然后报告说订单提交成功
。
如果不用异步方式,那么当邮件发送很耗时的时候,用户就要等很久才能看到成功提示,
因此我想,在保存了订单数据后,就用另一个线程来发送邮件,从而当前线程立即就可以向用户反馈订单提交成功的提示信息。
这个就像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就用了
因为线程池不会根据业务结束就销毁线程 而是一直让线程活着 节省销毁的时间
而且新任务来了 也不用等待 创建线程去应对 而是直接执行 节省时间 资源
自动注入 我们给容器中放入的线程池