同步调用
在单线程方式下,计算机是一台严格意义上的冯·诺依曼式机器,一段代码调用另一段代码时,必须等待这段被调用代码执行完返回结果后,调用方才能继续往下执行。
举例
你 喊 你朋友吃饭 ,你朋友在忙 ,你就一直在那等,等你朋友忙完了 ,你们一起去。
代码
public class A {
public void methodA()
{
System.out.println("this is class A method");
}
}
public class B {
public void methodB()
{
A a = new A();
a.methodA();
System.out.println("this is class B method");
}
}
public class Test {
public static void main(String[] args) {
B b = new B();
b.methodB();
}
}
output:
this is class A method
this is class B method
异步调用
asynchronous call(异步调用)
一个可以无需等待被调用函数的返回值就让操作继续进行的方法。
操作系统发展到今天已经十分精巧,线程就是其中一个杰作。操作系统把 CPU 处理时间划分成许多短暂时间片,在时间 T1 执行一个线程的指令,到时间 T2又执行下一线程的指令,各线程轮流执行,结果好象是所有线程在并肩前进。这样,编程时可以创建多个线程,在同一期间执行,各线程可以“并行”完成不同的任务。
在单线程方式下,计算机是一台严格意义上的冯·诺依曼式机器,一段代码调用另一段代码时,只能采用同步调用,必须等待这段代码执行完返回结果后,调用方才能继续往下执行。有了多线程的支持,可以采用异步调用,调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理。
举例
你 喊 你朋友吃饭 ,你朋友说知道了 ,待会忙完去找你 ,你就去做别的了。
应用场景
计算机中有些处理比较耗时。调用这种处理代码时,调用方如果站在那里苦苦等待,会严重影响程序性能。
例如,某个程序启动后如果需要打开文件读出其中的数据,再根据这些数据进行一系列初始化处理,程序主窗口将迟迟不能显示,让用户感到这个程序怎么等半天也不出来,太差劲了。借助异步调用可以把问题轻松化解:把整个初始化处理放进一个单独线程,主线程启动此线程后接着往下走,让主窗口瞬间显示出来。等用户盯着窗口犯呆时,初始化处理就在背后悄悄完成了。
异步调用用来处理从外部输入的数据特别有效。假如计算机需要从一台低速设备索取数据,然后是一段冗长的数据处理过程,采用同步调用显然很不合算:计算机先向外部设备发出请求,然后等待数据输入;而外部设备向计算机发送数据后,也要等待计算机完成数据处理后再发出下一条数据请求。双方都有一段等待期,拉长了整个处理过程。其实,计算机可以在处理数据之前先发出下一条数据请求,然后立即去处理数据。如果数据处理比数据采集快,要等待的只有计算机,外部设备可以连续不停地采集数据。如果计算机同时连接多台输入设备,可以轮流向各台设备发出数据请求,并随时处理每台设备发来的数据,整个系统可以保持连续高速运转。编程的关键是把数据索取代码和数据处理代码分别归属两个不同的线程。数据处理代码调用一个数据请求异步函数,然后径自处理手头的数据。待下一组数据到来后,数据处理线程将收到通知,结束 wait 状态,发出下一条数据请求,然后继续处理数据。
实现
调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理。
异步调用时,调用方不等被调方返回结果就转身离去,因此必须有一种机制让被调方有了结果时能通知调用方。
- 在同一进程中有很多手段可以利用,常用的手段是回调、互斥对象和消息。
- 如果调用方和被调方分属两个不同的进程,由于内存空间的隔阂,一般是采用 Windows消息发通知比较简单可靠,被调方可以借助消息本身向调用方传送数据。在java中可以通过向调用方发送完成消息、调用方定时检测等方法实现。
在使用异步调用时,不同线程共享代码或共享数据时容易出问题,要注意对共享域的访问时需要加锁,必要时甚至可以把异步函数转换为同步函数。方法很简单:调用异步函数后马上调用 wait 函数等在那里,待异步函数返回结果后再继续往下走。
代码
public class A implements Runnable {
@Override
public void run() {
System.out.println("methodA()执行中...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("methodA()执行完成。");
}
public void methodA(){
System.out.println("开始执行 methodA()");
Thread thread = new Thread(new A());
thread.start();
}
}
public class B {
public void methodB(){
System.out.println("开始执行 methodB()");
System.out.println("methodB()执行完成");
}
}
public class Main {
public static void main(String[] args) {
A a = new A();
B b = new B();
System.out.println("===========分割线1===========");
//methodA()是异步调用,通知methodA()执行后,继续执行后续指令,不等待methodA()执行完成
a.methodA();
System.out.println("===========分割线2===========");
b.methodB();
System.out.println("===========分割线3===========");
System.out.println("main()函数执行结束。\n");
}
}
output:
===========分割线1===========
开始执行 methodA()
===========分割线2===========
开始执行 methodB()
methodA()执行中...
methodB()执行完成
===========分割线3===========
main()函数执行结束。
methodA()执行完成。
回调
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。简而言之,回调函数就是允许用户把需要调用的函数的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。
- 意思 : 通过函数指针调用的函数
- 作用 : 对特定的事件或条件进行响应
举例
快下班了,你告诉朋友吃饭的时候叫你一起去。到了吃饭的时间,朋友叫你一起去吃饭。
在这个例子中,条件是到了吃饭的时间,回调函数是叫你一起去吃饭。
实现
⑴定义一个回调函数;
⑵提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;
⑶当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
扩展:回调函数是继承自C语言的,Java中没有函数指针的概念,所以需要在一个类中编写回调函数,使用时将包含回调函数的对象注册给调用者。
代码
/**
* 定义回调接口
* 抽象出回调接口,可以使代码的扩展性更好,也能满足更多的业务场景。
* @author yan
*
*/
public interface CallBack {
/**
* 异步请求执行成功后调用此方法
*/
public void onSuccess();
/**
* 异步请求执行失败后调用此方法
*/
public void onFailure();
}
public class AsyncTask implements Runnable {
String message;
CallBack callBack;
/**
* 异步发送消息,根据执行情况调用回调类中的对应函数
* @param msg
* @param callBack
*/
public void send(String msg, CallBack callBack){
message = msg;
this.callBack = callBack;
new Thread(this).start();//异步执行
}
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("发送消息:"+message);
} catch (InterruptedException e) {
e.printStackTrace();
callBack.onFailure();
}
callBack.onSuccess();
}
}
public class MsgSendCallback implements CallBack {
@Override
public void onSuccess() {
System.out.println("消息发送成功。");
}
@Override
public void onFailure() {
System.out.println("消息发送失败,请重试。");
}
}
public class Main {
public static void main(String[] args) {
AsyncTask sender = new AsyncTask();
sender.send("朋友,新年好!", new MsgSendCallback());
}
}