java异步回调有哪几种方式

为什么要使用异步回调

通常业务流程中会处理很多的事情,有一些是可以并行的执行的,这时候如果全部串行的执行这些业务是很耗时的,而如果使用异步调用,统一结果收集的方式将会极大的提高效率,进一步,如果需要获得这些并行业务的结果,那么通过异步回调来获取结果又是一个大的提升。

以下将从Java Future异步回调技术入手,然后介绍Guava Future,最后介绍一下Netty的异步回调技术

JOIN

讲Future异步回调之前,先看看join的方式

public class ThreadA extents Thread{
    public void run(){
        //do something in threadA    
    }
}

public class ThreadB extents Thread{
    public void run(){
        //do something in threadB    
    }
}

public class MainThread{
    public void main(String[] args){
        ThreadA threadA = new ThreadA();  
        ThreadB threadB = new ThreadB();
        
        threadA.start();
        threadB.start();
        
        threadA.join();
        threadB.join();
        
        System.out.print("main Thread finish");
    }
}

通过分别调用threadA和threadB的join方法,主线程可以等待直到两个线程任务执行完。但是这种方式的问题是主线程等待期间什么都不能做。并且无法获取threadA,threadB线程的执行结果,即使两个任务线程抛出异常,主线程也不会知道

public static class ThreadA extends Thread{

  @Override
  public void run() {
    System.out.println("aa");
  }
}

public static class ThreadB extends Thread{

  @SneakyThrows
  @Override
  public void run() {
    throw new PlatformOpeException(ExceptionEnum.COMMON_SUCCESS);
  }
}

public static void main(String[] args) throws InterruptedException {
  ThreadA threadA = new ThreadA();
  ThreadB threadB = new ThreadB();

  threadA.start();
  threadB.start();

  threadA.join();
  threadB.join();

  System.out.println("main thread finish");

}

Exception in thread "Thread-1" PlatformOpeException(exceptionCode=100000, exceptionMessage=操作成功, exceptionEnum=COMMON_SUCCESS)

aa

main thread finish

at com.xiaomi.mifi.policy.admin.store.PolicyDataRecordServiceFactory$ThreadB.run(PolicyDataRecordServiceFactory.java:61)

鉴于Runnable接口无法获取线程执行状态结果,JDK1.5之后出现了FutureTask和Callable两个接口

Future

FutureTask既然作为Callable的载体,那么他就需要一个outCome来存储Callable的返回值

使用FutureTask和Callable作为异步执行的案例代码

public class ThreadA implements Callable{
    public Integer call() throws Exception{
        return 1;
    }
}

public class ThreadB implements Callable{
    public Integer call() throws Exception{
        return 2;
    }
}

public static void main(String[] args){
    FutureTask aTask = new FutureTask<>(new ThreadA());
    Thread threadA = new Thread(aTask);
    
    FutureTask bTask = new FutureTask<>(new ThreadB());
    Thread threadB = new Thread(bTask);
    
    threadA.start();
    threadB.start();
    
    Integer aResult = aTask.get();
    Integer bResult = bTask.get();
    if(aResult == 1 && bResult == 1){
        System.out.print("main Thread finished here");    
    }
}

这里我们使用FutureTask和Callable解决了自任务状态的问题,但是对于异步执行还是和join一样,主线程需要一直阻塞,直到拿到结果。

在google的Guava中,通过ListenableFuture解决了异步调用的问题,让异步任务可以真正的“异步”,主线程可以做其他事情,直到拿到结果做依赖子任务结果的处理逻辑。

Guava-FutureCallBack

首先看看Guava中的FutureCallback接口

public interface FutureCallback{
    void onSuccess(@Nullable V var1);
    void onFailure(@Throwable var1);
}

通过FutureCallback我们可以在Callable任务执行完成之后,根据状态执行善后工作。

然后Guava通过ListenableFuture实现了Callable和FutureCallback结果回调之间的监控关系

public interface ListenableFuture extends Future{
    void addListener(Runnable r,Executor e);
}

也可以通过Future的addCallback方法将FutureCallback回调逻辑绑定到异步的ListenableFuture任务。(ListenableFuture就是自任务的FutureTask,FutureCallback是需要监听子线程状态的回调函数,比如主线程有一个标识符表示子线程的状态,通过while循环不断的轮询这个状态,如果子线程未准备好,那么继续主线程的逻辑。这个状态在FutureCallback中会根据子线程的状态进行变更)

Futures.addCallback(listenableFuture,new FutureCallback(){
    public void onSuccess(Integer result){
         //.....子线程状态标识符变更       
    }
    
    public void onFailure(Throwable t){
        //.....子线程状态标识符变更    
    }
})

Guava的异步回调除了需要定义线程池以外还需要定义它自己的线程池,用来做回调使用的线程池

public class ThreadA implements Callable{
    
    public Integer call(){
        return 1;    
    }
}

public class ThreadB implements Callable{
    
    public Integer call(){
        return 2;    
    }
}


public static void main(String[] args){
    int aResult = 0;
    int bResult = 0;
    ListenableExecutorService gPool = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10));
    ListenableFuture aFuture = gPool.submit(new ThreadA());
    Futures.addCallback(aFuture,new FutureCallback(){
        public void onSuccess(Integer result){
             aResult = result;//这里是回调函数的实现逻辑              
        }
    
        public void onFailure(Throwable t){
            aResult = -1;//这里是回调函数的实现逻辑          
        }
    });
    
    ListenableFuture bFuture = gPool.submit(new ThreadB());
    Futures.addCallback(bFuture,new FutureCallback(){
        public void onSuccess(Integer result){
             bResult = result;//这里是回调函数的实现逻辑       
        }
    
        public void onFailure(Throwable t){
            bResult = -1;//这里是回调函数的实现逻辑          
        }
    });
    
    while(true){
        if(aResult == -1 || bResult == -1){
            System.out.print("sub thread run fail");
            break;        
        }else if(aResult == 1 && bResult == 1){
            System.out.print("sub thread run success");
            break;             
        }else{
            System.out.print("sub thread not finish");             
        }    
    }
    System.out.print("main thread finish");   
}

Gauva 是异步非阻塞的,FutureTask是异步阻塞的。

Netty-GenericFutureListener

Netty作为高性能的网络框架,它内部使用了大量的异步回调的机制,在Netty中异步回调的实现是通过GenericFutureListener来定义的(之所以是定义,是因为Netty中有很多的事件,不同的事件会有不同的FutureListener,而这些基本都是基于GenericFutureListener)

public interface GenericFutureListener> extends EventListener{
    void operationComplet(F var) throws Exception;
}

Netty中有很多的事件,不同的事件有不同的Future,比如ChannelFuture用来处理IO操作的异步任务,也就是在IO操作完成后,需要执行回调操作,就需要使用ChannelFuture接口

ChannelFuture future = bootstrap.connect(new InetSocketAddress("www.baidu.com"))
future.addListener(new ChannelFutureListener(){
    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception{
        if(channelFuture.isSuccess()){
            sysout("success");//回调逻辑                    
        }    
    }
})

 

 

 

 

你可能感兴趣的:(Netty,java,java,多线程,异步回调)