1. 同步和异步的理解

同步和异步关注的是消息通讯机制(synchronous communication / asynchronous communication )。

同步就是调用某个东西时,调用方得等待这个调用返回结果才能继续往后执行。

异步和同步相反,调用方不会等待得到结果,而是在调用者发出调用后,调用者可以继续执行后续操作,被调用者通过状态来通知调用者,或者调用者通过回调函数来处理这个调用的结果。

图解:

  • 同步执行

        

异步消息如何保证事务一致性_System

  • 异步执行

         

异步消息如何保证事务一致性_子线程_02

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 () 都是等待异步操作执行完成,那么它们有什么区别呢?

  1. sync () 会抛出异常,建议使用 sync ();
  2. 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 () 方法会返回异常信息的内容。

异步消息如何保证事务一致性_异步操作_03

 

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 主要存在以下几个缺陷:

  1. 只允许手动通过 get () 来检查对应的操作是否已经完成,它是堵塞直到子线程完成执行并且返回结果;
  2. 只有 isDone () 方法判断一个异步操作是否完成,但是对于完成的定义过于模糊,JDK 文档指出正常终止、抛出异常、用户取消都会使 isDone () 方法返回真。并不能很好的区分到底是哪种状态。
  3. 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

结论总结:

  1. 说明了 Java 并发包的 Future 要想获取异步执行结果,必须手工调用 get () 方法,此时虽然能获取执行结果,但是无法知道执行结果是成功还是失败;
  2. 使用 get () 获取执行结果,但是 get () 后面的业务则被堵塞,直到后面执行完毕才会往下执行,失去了异步操作提高执行效率的意义了。