Java回调实现异步 (转)
出处: Java回调实现异步
在正常的业务中使用同步线程,如果服务器每处理一个请求,就创建一个线程的话,会对服务器的资源造成浪费。因为这些线程可能会浪费时间在等待网络传输,等待数据库连接等其他事情上,真正处理业务逻辑的时间很短很短,但是其他线程在线程池满了之后又会阻塞,等待前面的线程处理完成。而且,会出现一个奇怪的现象,客户端的请求被阻塞,但是cpu的资源使用却很低,大部分线程都浪费在处理其他事情上了。所以,这就导致服务器并发量不高。
而异步,则可以解决这个问题。
我们可以把需要用到cpu的业务处理使用异步来实现,这样其他请求就不会被阻塞,而且cpu会保持比较高的使用率。
今天就学习了使用回调来实现异步的方法。我们设想一个情景,A是处理业务的一个步骤,A需要解决一个问题,这时候A可以问B,让B来告诉A答案,这期间,A可以继续做自己的事情,而不用因为B做的事而阻塞。于是,我们想到给B设置一个线程,让B去处理耗时的操作,然后处理完之后把结果告诉A。所以这个问题的要点就在于B处理完之后如何把结果告诉A。我们可以直接在A中写一个方法对B处理完的结果进行处理,然后B处理完之后调用A这个方法。这样A调用B去处理过程,B调用A的C方法去处理结果就叫做回调。
package CallBack; public interface CallBack {
/*
*A处理结果的方法,为什么要写这个接口呢?
*因为可能不止A需要用到B的处理过程,如果很多地方需要用到B
* 那么传入B的方法就不可能只传A类,所以要定义一个接口,
* 传入B的处理方法的参数就是这个接口对象
* */
public void solve(String result);
}
package CallBack; public class A implements CallBack {
private B b; public A(B b){
this.b=b;
} //A需要解决一个问题,所以他把问题交给B处理,B单独创建一个线程,不影响A的运行
public void ask(final String question){
System.out.println("A问了B一个问题");
new Thread(()->{
//B想要帮A处理东西,就必须知道谁让自己处理的,所以要传入a,也要知道a想处理什么,所以要传入question
b.executeMessage(A.this,question);
}).start();
//A把要处理的事情交给b之后,就可以自己去玩耍了,或者去处理其他事情
play();
} public void play(){
System.out.println("我要逛街去了");
} //A拿到了B处理完成的结果,可以进行一些操作,比如把结果输出
@Override
public void solve(String result) {
System.out.println("B告诉A的答案是--》"+result);
} }
package CallBack; public class B {
public void executeMessage(CallBack callBack,String question){
System.out.println(callBack.getClass()+"问的问题--》"+question);
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String result="答案是2";
callBack.solve(result);
}
}
package CallBack; public class test {
public static void main(String[] args) {
B b=new B();
A a=new A(b);
a.ask("1+1=?");
}
}
console结果:
运行结果:
A问了B一个问题
我要逛街去了
class CallBack.A问的问题--》1+1=?
B告诉A的答案是--》答案是2 Process finished with exit code 0
异步回调的实现依赖于多线程或者多进程
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。
多线程中的“回调” (JDK8之前)
Java多线程中可以通过callable和future或futuretask结合来获取线程执行后的返回值。实现方法是通过get方法来调用callable的call方法获取返回值。
其实这种方法本质上不是回调,回调要求的是任务完成以后被调用者主动回调调用者的接口。而这里是调用者主动使用get方法阻塞获取返回值。
一般情况下,我们会结合Callable和Future一起使用,通过ExecutorService的submit方法执行Callable,并返回Future。
public class 多线程中的回调 {
//这里简单地使用future和callable实现了线程执行完后
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
System.out.println("call");
TimeUnit.SECONDS.sleep(1);
return "str";
}
});
//手动阻塞调用get通过call方法获得返回值。
System.out.println(future.get());
//需要手动关闭,不然线程池的线程会继续执行。
executor.shutdown(); //使用futuretask同时作为线程执行单元和数据请求单元。
FutureTask<Integer> futureTask = new FutureTask(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println("dasds");
return new Random().nextInt();
}
});
new Thread(futureTask).start();
//阻塞获取返回值
System.out.println(futureTask.get());
}
@Test
public void test () {
Callable callable = new Callable() {
@Override
public Object call() throws Exception {
return null;
}
};
FutureTask futureTask = new FutureTask(callable); }
}
比起future.get(),其实更推荐使用get (long timeout, TimeUnit unit) 方法,设置了超时时间可以防止程序无限制的等待future的结果。
CompletableFuture介绍(JDK8)
Future模式的缺点
Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。
要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。
CompletableFuture
Netty、Guava分别扩展了Java 的 Future 接口,方便异步编程。
Java 8新增的CompletableFuture类正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特征,还提供了其它强大的功能,让Java拥有了完整的非阻塞编程模型:Future、Promise 和 Callback(在Java8之前,只有无Callback 的Future)。
CompletableFuture能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。
CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。
相关详细API内容介绍: 理解Java8里面CompletableFuture异步编程
Java回调实现异步 (转)的更多相关文章
- 深入浅出: Java回调机制(异步)
一.什么是回调 回调,回调.要先有调用,才有调用者和被调用者之间的回调.所以在百度百科中是这样的: 软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用.回调和异步调用. 回调 ...
- Java回调实现异步
在正常的业务中使用同步线程,如果服务器每处理一个请求,就创建一个线程的话,会对服务器的资源造成浪费.因为这些线程可能会浪费时间在等待网络传输,等待数据库连接等其他事情上,真正处理业务逻辑的时间很短很短 ...
- Java回调方法详解
回调在维基百科中定义为: 在计算机程序设计中,回调函数,是指通过函数参数传递到其他代码的,某一块可执行代码的引用. 其目的是允许底层代码调用在高层定义的子程序. 举个例子可能更明白一些:以Androi ...
- 【java回调】java两个类之间的回调函数传递
背景交代:熟悉用js开发的cordovaAPP:对java一窍不通的我,老师让做一个监测用户拍照事件的功能,无奈没有找到现成的库,无奈自己动手开发java插件~~0基础java GreenHand,祝 ...
- java回调机制及其实现(转)
1. 什么是回调函数 回调函数,顾名思义,用于回调的函数.回调函数只是一个功能片段,由用户按照回调函数调用约定来实现的一个函数.回调函数是一个工作流的一部分,由工作流来决定函数的调用(回调)时机.回调 ...
- Java回调机制解读
模块间调用 在一个应用系统中,无论使用何种语言开发,必然存在模块之间的调用,调用的方式分为几种: (1)同步调用 同步调用是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b(),一直等 ...
- java 回调函数解读
模块间调用 在一个应用系统中,无论使用何种语言开发,必然存在模块之间的调用,调用的方式分为几种: (1)同步调用 同步调用是最基本并且最简单的一种调用方式,类A的方法a()调用类B的方法b(),一直等 ...
- Java回调机制总结
调用和回调机制 在一个应用系统中, 无论使用何种语言开发, 必然存在模块之间的调用, 调用的方式分为几种: 1.同步调用 同步调用是最基本并且最简单的一种调用方式, 类A的方法a()调用类B的方法b( ...
- Java回调函数的理解与实现
回调函数,或简称回调,是指通过函数参数传递到其它代码的,某一块可执行代码的引用.这一设计允许了底层代码调用在高层定义的子程序. 在Java里面,我们使用接口来实现回调.举个例子 所谓的回调,就是程序员 ...
随机推荐
- Ant环境搭建
1.上传安装包到linux服务器 2.解压缩 tar zxvf apache-ant-1.10.1-bin.tar.gz 3.修改环境变量 vim /etc/profile 添加以下内容 expor ...
- Java中可变参数
从java5开始出现了可变参数,这是对java方法及数组的拓展! 方法中可以接受的参数不再是固定个数的,而是随着具体需求传递的多少来决定. 定义格式: 返回值类型 方法名(参数类型 ... 形式参数 ...
- Failed to execute goal maven-gpg-plugin 1.5 Sign
问题描述: 解决办法:跳过maven-gpg-plugin <build> <pluginManagement> <plugins> <plugin> ...
- linux环境中关闭tomcat,通过shutdown.sh无法彻底关闭--线程池
最近测试环境上测试的项目通过shutdown.sh始终无法彻底关闭. 之前临时解决方法两种: 第一:通过ps -ef|grep tomcat查看到tomcat的进程直接使用kill来杀死进程. 第二: ...
- ansible的剧本
ansible的playbook的介绍-yaml ansible的playbook是使用yaml语言写的 YAML标记语言介绍YAML是一个可读性高的用来表达资料序列的格式.YAML参考了其他多种语言 ...
- pycharm中模块不能导入的问题
在pycharm中发现模块老是导入不成功 只能以这样的映射的方式 现在才知道: 模块的标志符可以由字母.数字.下划线组成,但是, 不能以数字开头,如果在给python文件起名时,以数字开头是无法在py ...
- linux下如何查看当前内核的配置?
答: zcat /proc/config.gz 当然有个前提条件,需要打开内核的以下两个选项(CONFIG_IKCONFIG和CONFIG_IKCONFIG_PROC): General setup ...
- 图解Python 【第一篇】:Python基础1
本节内容一览图 一.Python简介 Python前世今生 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间, ...
- Tomcat多实例集群架构 安全优化和性能优化
Tomcat多实例 复制tomcat目录 /usr/local/tomcat1 /usr/local/tomcat2 修改多实例配置文件 #创建多实例的网页根目录 mkdir -p /data/www ...
- 开发-组件-GemBox:百科
ylbtech-开发-组件-GemBox:百科 6000+ Companies Trust GemBox With .NET and Java File Format Components GemBo ...