1. 同步和异步的理解
同步和异步关注的是消息通讯机制(synchronous communication / asynchronous communication )。
同步就是调用某个东西时,调用方得等待这个调用返回结果才能继续往后执行。
异步和同步相反,调用方不会等待得到结果,而是在调用者发出调用后,调用者可以继续执行后续操作,被调用者通过状态来通知调用者,或者调用者通过回调函数来处理这个调用的结果。
图解:
- 同步执行
- 异步执行
2. 同步异步与阻塞非阻塞的区别
同步和异步强调的是消息的通讯机制。所谓同步就是主线程发出一个“调用“时,该“调用”在没有得到结果之前,该“调用”就不返回。但是一旦调用返回,就是得到的调用的结果。
异步则相反,“调用”发出之后,这个调用就直接返回了,所有没有返回结果。换句话说就是,当一个异步过程调用发出后,调用者不会立刻得到结果,而是在“调用”发出后,“被调用者”通过状态,通知来告诉调用者或者通过回调函数来处理这个调用。
阻塞和非阻塞强调的是程序在等待调用结果时的状态。阻塞调用是指调用结果返回之前,当前线程会被挂起,直到调用线程返回结果。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。
对用同步调用来说,很多的时候当前线程还是激活的状态,只是从逻辑上当前函数没有返回而已,即同步等待时什么都不干,白白占用着资源。
2. Netty中异步理解
在Netty中所有的IO操作都是异步的,不能立刻得到IO操作的执行结果。如果我们要获取IO操作的结果,就需要注册一个监听器来监听其执行结果。在Java中的并发编程当中可以通过Future来进行异步结果的监听,但是在Netty当中时通过ChannelFuture来实现异步结果的监听。通过注册一个监听的方式来进行监听,当操作执行成功或者失败时监听会自动触发注册的监听事件。
ChannelFuture在开发当中时需要经常用到的,可以用来监听客户端连接服务端的结果反馈,Netty是异步操作,无法知道什么时候执行完成,因此可以通过ChannelFuture来进行执行结果的监听。在Netty当中的bind,writer,connect等操作都会简单的返回一个ChannelFuture。
核心方法:
序号 | 方法 | 描述 |
1 | addListener | 注册监听器,当操作已完成 (isDone 方法返回完成),将会通知指定的监听器;如果 Future 对象已完成,则通知指定的监听器 |
2 | removeListener | 移除监听器 |
3 | sync | 等待异步操作执行完毕 |
4 | await | 等待异步操作执行完毕 |
5 | isDone | 判断当前操作是否完成 |
6 | isSuccess | 判断已完成的当前操作是否成功 |
7 | isCancellable | 判断已完成的当前操作是否被取消 |
8 | cause | 获取已完成的当前操作失败的原因 |
sync () 和 await () 都是等待异步操作执行完成,那么它们有什么区别呢?
- sync () 会抛出异常,建议使用 sync ();
- await () 不会抛出异常,主线程无法捕捉子线程执行抛出的异常;
3.深入了解 ChannelFuture
3.1生命周期说明
Future 可以通过四个核心方法来判断任务的执行情况。
状态 | 说明 |
isDone() | 任务是否执行完成,无论成功还是失败 |
isSuccess() | 任务是否执行采购 |
isCancelled() | 任务是否被取消 |
cause() | 获取执行异常信息 |
执行过程状态的改变说明
当一个异步任务操作开始的时候,一个新的 future 对象就会被创建。
在开始的时候该 future 是处于未完成的状态,也就是说,isDone ()=false、isSuccess ()=false、isCancelled ()=false;
只要该任务中任何一种状态结束了,无论是说成功、失败、或者被取消,那么整个 Future 就会被标记为已完成。
注意的是,如果执行失败那么 cause () 方法会返回异常信息的内容。
3. 深入了解 ChannelFuture
3.1 生命周期说明
Future 可以通过四个核心方法来判断任务的执行情况。
状态 | 说明 |
isDone() | 任务是否执行完成,无论成功还是失败 |
isSuccess() | 任务是否执行采购 |
isCancelled() | 任务是否被取消 |
cause() | 获取执行异常信息 |
执行过程状态的改变说明
当一个异步任务操作开始的时候,一个新的 future 对象就会被创建。在开始的时候该 future 是处于未完成的状态,也就是说,isDone ()=false、isSuccess ()=false、isCancelled ()=false;只要该任务中任何一种状态结束了,无论是说成功、失败、或者被取消,那么整个 Future 就会被标记为已完成。注意的是,如果执行失败那么 cause () 方法会返回异常信息的内容。
ChannelFuture channelFuture=bootstrap.connect("127.0.0.1",80);
channelFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if(future.isDone()){
if(future.isSuccess()){
System.out.println("执行成功...");
}else if(future.isCancelled()){
System.out.println("任务被取消...");
}else if(future.cause()!=null){
System.out.println("执行出错:"+future.cause().getMessage());
}
}
}
});
3.2 ChannelFuture 父接口说明
ChannelFuture 的类继承结构,具体如下所示:
public interface ChannelFuture extends Future<Void> {
}
public interface Future<V> extends java.util.concurrent.Future<V> {
}
通过上面的继承关系,我们可以清晰的知道 ChannelFuture 其实最顶层的接口是来自 java 并发包的 Future,java 并发包下的 Future 需要手工检查执行结果是否已经完成,非常的繁琐,因此 Netty 把它进行了封装和完善,变成了自动的监听,用起来变的非常的简单。
java 并发包下的 Future 主要存在以下几个缺陷:
- 只允许手动通过 get () 来检查对应的操作是否已经完成,它是堵塞直到子线程完成执行并且返回结果;
- 只有 isDone () 方法判断一个异步操作是否完成,但是对于完成的定义过于模糊,JDK 文档指出正常终止、抛出异常、用户取消都会使 isDone () 方法返回真。并不能很好的区分到底是哪种状态。
- get () 方法是堵塞的,必须等待子线程执行完成才能往下执行。
//1.定义一个子线程,实现 Callable 接口
public class ThreadTest implements Callable<Integer>{
@Override
public Integer call(){
//打印
System.out.println(">>>>>>>>子线程休眠之前");
//休眠5秒
Thread.sleep(5000);
//打印
System.out.println(">>>>>>>>子线程休眠之后");
return 1;
}
}
//2.调用子线程处理
public static void main(String[] args){
ThreadTest t=new ThreadTest();
FutureTask<Integer> future=new FutureTask<Integer>(t);
//2.1.开始执行子线程
new Thread(future).start();
//2.2.手工返回结果
int result=future.get();
System.out.println(">>>>>>>>执行结果:"+result);
//2.3.操作数据库
userDao.updateStatus("1");
}
执行结果:
>>>>>>>>子线程休眠之前
>>>>>>>>子线程休眠之后
>>>>>>>>执行结果:1
结论总结:
- 说明了 Java 并发包的 Future 要想获取异步执行结果,必须手工调用 get () 方法,此时虽然能获取执行结果,但是无法知道执行结果是成功还是失败;
- 使用 get () 获取执行结果,但是 get () 后面的业务则被堵塞,直到后面执行完毕才会往下执行,失去了异步操作提高执行效率的意义了。