回调+异步任务处理
回调+异步任务处理
zhangzhang回调+异步任务处理
- 模拟 “客户端发消息给服务端,服务端处理后(延迟 5 秒)回调通知客户端” 的流程
1 | import java.lang.Thread; |
- 输出:
1 | 客户端:准备发送消息 -> [Server, Hello! 我是客户端~] |
这个代码是Java 中 “回调模式” 实现异步任务处理的经典示例,核心是模拟 “客户端发消息给服务端,服务端处理后(延迟 5 秒)回调通知客户端” 的流程。我分模块拆解,帮你彻底搞懂~
一、核心概念先理清
- 回调(Callback):A 调用 B 的方法时,把 A 自己的引用(或接口实现)传给 B;B 处理完后,调用 A 的方法通知结果(相当于 “反向调用”)。
- 异步(Async):客户端发消息后不用等待服务端处理完成,继续做自己的事;服务端处理完后再 “回调” 客户端告知结果。
二、代码模块逐行解析
1. 回调接口 CSCallback
1 | interface CSCallback { // 定义回调的“协议”:服务端处理完后要调用这个方法 |
- 作用:约定服务端处理完成后,要给客户端返回什么格式的结果(这里是
status字符串)。 - 客户端需要实现这个接口,才能让服务端 “反向调用” 自己的方法。
2. 服务端类 Server
1 | class Server { // 服务端:接收客户端消息、处理、回调通知 |
- 核心方法
getClientMsg:- 参数 1
csCallback:客户端的接口实现对象(因为客户端实现了CSCallback,所以能传进来)。 - 参数 2
msg:客户端发送的消息内容。 - 逻辑:接收消息 → 模拟处理延迟(5 秒) → 处理完成后,调用客户端的
process方法(这就是 “回调” 的核心动作)。
- 参数 1
3. 客户端类 Client(实现回调接口)
1 | class Client implements CSCallback { // 客户端:实现回调接口,接收服务端的通知 |
核心点:
implements CSCallback:客户端必须实现回调接口,这样服务端才能调用它的process方法。sendMsg1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
方法:
- 用`new Thread`包裹对服务端的调用 → **异步执行**(主线程不会被服务端的 5 秒延迟阻塞)。
- `Client.this`:把 “当前客户端对象” 传给服务端(因为客户端实现了`CSCallback`,所以能作为参数传递)。
3. `process`方法:**服务端处理完成后,会回调这个方法**,打印服务端返回的状态。
#### 4. 测试类 `TestCallBack`
```java
public class TestCallBack {
public static void main(String[] args) {
Server server = new Server(); // 创建服务端
Client client = new Client(server); // 创建客户端(关联服务端)
client.sendMsg("Server,Hello~"); // 客户端发消息
System.out.println("异步处理不会阻塞主线程"); // 主线程继续执行
}
}
执行流程:
- 创建服务端、客户端对象。
- 客户端调用
sendMsg发消息 → 启动新线程调用服务端getClientMsg。 - 主线程不用等,直接打印 “异步处理不会阻塞主线程”。
- 服务端处理 5 秒后,回调客户端的
process方法,打印状态。
三、最终运行结果(顺序参考)
1 | 客户端:准备发送消息 -> [Server, Hello! 我是客户端~] |
四、核心亮点总结
- 回调的本质:客户端把自己(接口实现)传给服务端,服务端处理完后 “反向调用” 客户端的方法。
- 异步的实现:用
Thread包裹服务端调用,让服务端的处理在新线程中执行,主线程不阻塞。 - 解耦:服务端只依赖
CSCallback接口,不用关心具体是哪个客户端;客户端只依赖接口,不用关心服务端的具体实现。
问题1:public void getClientMsg(CSCallback csCallback, String msg) {为什么接口可以作为参数
问题2:csCallback.process(status);接口为什么会形成对象,还可以调用函数
要搞懂这两个问题,核心是理解 Java 接口的 “多态特性” 和 “接口不能直接实例化,但可以指向实现类对象” 这两个关键点。我用通俗的语言 + 代码示例拆解,保证一看就懂。
问题 1:为什么接口可以作为方法参数?
先给结论:接口作为参数,本质是接收 “实现了该接口的类的对象” —— 这是 Java 多态的核心应用,目的是解耦(方法不依赖具体类,只依赖接口)。
举个生活例子
比如定义一个 “支付接口 Pay”,里面有pay()方法;然后有Alipay、WechatPay两个类实现这个接口。
现在写一个 “收银台” 方法checkout(Pay pay),参数是 Pay 接口 —— 这个方法不需要关心用户用支付宝还是微信,只要传 “实现了 Pay 接口的对象” 就行。
回到代码层面解析
1 | // 接口 |
底层原理
- Java 中 “接口作为参数”,并不是要求传 “接口本身的对象”(接口根本不能 new),而是要求传 “实现了该接口的类的实例”。
- 这是多态的体现:
CSCallback csCallback这个参数,是一个 “引用变量”,它可以指向任何实现了CSCallback接口的类的对象(比如 Client 对象)。 - 好处:服务端的
getClientMsg方法不需要关心传进来的是哪个类(比如 Client1、Client2),只要这个类实现了CSCallback,就能调用接口里的process方法 —— 实现了 “面向接口编程”,解耦了服务端和具体的客户端类。
反例(为什么要这么设计?)
如果不用接口做参数,服务端方法就要写死接收 Client 类:
1 | // 耦合度极高!如果换一个Client2类,这个方法就用不了了 |
而用接口做参数,不管是 Client、Client2,只要实现了 CSCallback,都能传给这个方法 —— 这就是接口作为参数的核心价值。
问题 2:csCallback.process (status); 接口为什么会形成对象,还可以调用函数?
先纠正一个认知:接口本身不会形成对象! 你看到的csCallback不是接口对象,而是 “实现了接口的类的对象”,只不过用接口类型的变量指向它而已。
分步拆解
第一步:csCallback变量的本质
1 | // 服务端方法里的参数:接口类型的引用变量 |
这个变量不是 “接口对象”,而是一个 “引用”(类似指针),它的作用是:指向任何实现了 CSCallback 接口的类的实例。
第二步:调用方法时,传入的是 “实现类对象”
1 | Client client = new Client(); // 创建Client对象(实现了CSCallback) |
此时,方法内部的csCallback变量,指向的是Client类的实例(真实对象是 Client,不是 CSCallback 接口)。
第三步:调用csCallback.process(status)的底层逻辑
1 | csCallback.process(status); // 实际调用的是Client类重写的process方法 |
因为csCallback指向的是 Client 对象,所以调用process方法时,会执行 Client 类中重写的process方法 —— 这是多态的 “动态绑定”:编译时看接口类型,运行时看实际指向的对象类型。
用代码验证(最直观)
1 | public class Test { |
输出结果:
1 | Client |
核心结论
csCallback不是 “接口对象”,而是 “接口类型的引用变量”,它指向的是实现了该接口的类的对象(比如 Client 对象)。能调用
1
process
方法,是因为:
- 接口中定义了
process方法(约定了必须实现); - 实际指向的对象(Client)重写了这个方法;
- 运行时会动态找到并执行 Client 的
process方法。
- 接口中定义了


