JAVA异步回调模式:从Thread join到FutureTask,再到Guava异步回调、Netty异步回调 //高并发

博文所在专栏里有更多相关内容,如JAVA NIO、Reactor反应器模式等,欢迎阅读与交流。
文字来源于读书笔记及个人心得,可能有引用其他博文,若引用了你的文字请联系我,我会加上来源,或者删除相关内容。

四 Future异步回调模式

(一)join合并

join操作的作用,就是完成异步阻塞的工作——阻塞当前的线程,直到异步的并发线程的执行完成。

但join合并有一个很大的问题,就是没有返回值。

public static void main(String args[]) {
    Thread hThread = new HotWarterThread();
    Thread wThread = new WashThread();
    hThread.start();
    wThread.start();
    try {
        // 合并烧水-线程
        hThread.join();
        // 合并清洗-线程
        wThread.join();

        Thread.currentThread().setName("主线程");
        Print.tcfo("泡茶喝");

    } catch (InterruptedException e) {
        Print.tcfo(getCurThreadName() + "发生异常被中断.");
    }
    Print.tcfo(getCurThreadName() + " 运行结束.");
}

join方法的三个重载版本:

(1)void join(): 等待乙方线程执行结束,甲方线程重启执行。

(2)void join(long millis): 等待乙方线程执行一段时间,最长等待时间为 millis 毫秒。超过millis 毫秒后,不论乙方是否结束,甲方线程重启执行。

(3)void join(long millis, int nanos): 等待乙方线程执行一段时间,最长等待时间为 millis 毫秒,加nanos 纳秒。超过时间后,不论乙方是否结束,甲方线程重启执行。

强调一下容易混淆的几点:

(1)join方法是实例方法,需要使用线程句柄去调用,如thread.join();

(2)执行到join代码的时候,不是thread所指向的线程阻塞,而是当前线程阻塞;

(3)thread线程代表的是被合并线程(乙方),当前线程阻塞线程(甲方)。当前线程让出CPU,进入等待状态。

(4)只有等到thread线程执行完成,或者超时,当前线程才能启动执行。

(二)FutureTask系列类

与join()相比,通过FutureTask的get方法可以获取异步结果,然而这个过程中,主线程也会被阻塞的,并不支持非阻塞异步。

了解FutureTask之前先认识下Callable和Future接口

Callable接口

Java语言在1.5 版本之后重新定义了一个新的、类似Runnable的接口——Callable接口,位于java.util.concurrent包中,将run方法改为了call方法,并且带上了返回值,允许异常抛出。

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

——不能直接代替Runnable作为Thread的传入参数

Future接口

主要提供了3大功能:

(1)获取并发的任务完成后的执行结果。

(2)能够取消并发执行中的任务;

(3)判断并发任务是否执行完成;

Future接口的代码如下:

public interface Future<V> {
    //取消执行
    boolean cancel(boolean mayInterruptRunning);
    //任务是否已取消
    boolean isCancelled();
    //任务是否已完成
    boolean isDone();
    //阻塞地获取并发任务执行的结果,直到并发任务执行完成。
    V get() throws InterruptedException, ExecutionException;    
    //阻塞地获取并发任务执行的结果,直到并发任务执行完成或阻塞时间超过超时时间。
    V get(long timeout,TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

FutureTask 类

位于 java.util.concurrent包,构造函数的参数为 Callable,并且间接的继承了Runnable接口。
在这里插入图片描述

既能当做一个Runnable 作为 target ,直接被Thread执行;也能作为Future用来去取得Callable的计算结果。

//构造器源码
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}
FutureTask内部有:

一个 Callable类型的成员,从构造器传入,用来保存并发执行的 Callable类型的任务。

一个 run方法,是Runnable接口在FutureTask内部的实现,该方法会执行到callable成员的call方法。

一个 Object 类型的重要成员:outcome,用来保存callable成员call方法的执行结果,供FutureTask类的get实例方法获取。

示例:

static class HotWarterJob implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        try {
            System.out.println("洗好水壶");
            System.out.println("灌上凉水");
            System.out.println("放在火上");

            //线程睡眠一段时间,代表烧水中
            Thread.sleep(SLEEP_GAP);
            System.out.println("水开了");

        } catch (InterruptedException e) {
            System.out.println(" 发生异常被中断.");
            return false;
        }
        System.out.println(" 运行结束.");
        return true;
    }
}

public static void main(String args[]) {
    Thread.currentThread().setName("主线程");
    
    Callable<Boolean> hJob = new HotWarterJob();
    FutureTask<Boolean> hTask = new FutureTask<Boolean>(hJob);
    Thread hThread = new Thread(hTask, "** 烧水-Thread");
    hThread.start();
    
    try {
        //hThread.join();
        Boolean warterOk = hTask.get();
    } catch (InterruptedException e) {
        System.out.println(getCurThreadName() + "发生异常被中断.");
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    System.out.println(getCurThreadName() + " 运行结束.");
}

(三)Guava 的异步回调

Guava 引入了异步回调机制,可以使用回调函数 对异步线程的结果进行处理 ,而 不用阻塞 地等待执行结果。

google 提供的扩展包 Guava,位于com.google.common.util.concurrent

使用流程

  • 创建Java的 Callable的异步任务实例:

    • Callable<Boolean> hJob = new HotWarterJob();
  • 创建java线程池:

    • //java 线程池
      ExecutorService jPool = Executors.newFixedThreadPool(10);
      
  • 通过java线程池获取Guava线程池:

    • //Guava线程池
      ListeningExecutorService gPool = MoreExecutors.listeningDecorator(jPool);
      
  • 提交异步任务到Guava线程池,获取ListenableFuture 实例:

    • ListenableFuture<Boolean> hFuture = gPool.submit(hJob);
  • 创建回调的 FutureCallback 实例,通过Futures.addCallback,将回调逻辑绑定到ListenableFuture 实例

    • Futures.addCallback( hFuture ,  new FutureCallback<Boolean>(){
          public void onSuccess(Boolean r){
      	//成功时候的回调逻辑
          }
          public void onFailure(Throwable t){
          //异常时候的回调逻辑
          }
      });
      
  • 完成以上步骤,当异步逻辑执行完成后,就会回调FutureCallback 实例中的回调代码。

FutureCallback: Future回调接口

FutureCallback 是一个新增的接口,用来填写回调逻辑

public interface FutureCallback<V> {
    //在异步线程执行成功时回调
    void onSuccess(@Nullable V var1);
    //在异步线程执行异常时回调
    void onFailure(Throwable var1);
}

ListenableFuture:可监听的Future接口

ListenableFuture 是 Guava 对java 的Future接口的扩展

package com.google.common.util.concurrent;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
public interface ListenableFuture<V> extends Future<V> {
    //将FutureCallback回调逻辑,绑定到异步线程上
    void addListener(Runnable var1, Executor var2);
}
绑定回调

addListener方法无法被直接调用,需借助Guava的Futures 工具类的addCallback 静态方法

//hFuture为ListenableFuture实例
Futures.addCallback( hFuture ,  new FutureCallback<Boolean>(){
    public void onSuccess(Boolean r){
	//成功时候的回调逻辑
    }
    public void onFailure(Throwable t){
    //异常时候的回调逻辑
    }
});
获取ListenableFuture实例

向Guava线程池提交Callable实例。

Netty的异步回调

与Guava一样有自己的异步回调体系:

  • 继承Java的Future接口,得到属于自己的同名的Future接口,位于io.netty.util.concurrent ,一般不会直接使用,而是使用子接口,如 ChannelFuture 接口
  • 引入新接口GenericFutureListener,监听异步任务的监听器

Netty的Future接口

一般不会直接使用,而会使用其子类,例如ChannelFuture

public interface Future<V> extends java.util.concurrent.Future<V> {
    // 任务执行成功,返回true
    boolean isSuccess();
    // 任务被取消,返回true
    boolean isCancellable();
    // 异步任务执行失败,返回异常
    Throwable cause();
    // 添加Listener,异步非阻塞处理回调结果
    Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
    Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
    // 移除Listener
    Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
    Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
    // 同步阻塞等待任务结束;执行失败话,会将“异常信息”重新抛出来
    Future<V> sync() throws InterruptedException;
    Future<V> syncUninterruptibly();
    // 同步阻塞等待任务结束,和sync方法一样,只不过不会抛出异常信息
    Future<V> await() throws InterruptedException;
    Future<V> awaitUninterruptibly();
    boolean await(long timeout, TimeUnit unit) throws InterruptedException;
    boolean await(long timeoutMillis) throws InterruptedException;
    boolean awaitUninterruptibly(long timeout, TimeUnit unit);
    boolean awaitUninterruptibly(long timeoutMillis);
    // 非阻塞,获取执行结果
    V getNow();
    // 取消任务
    @Override
    boolean cancel(boolean mayInterruptIfRunning);
}
ChannelFuture

ChannelFuture 子接口表示 Channel 通道 I/O 操作的异步任务;如果在 Channel 的异步 I/O 操作完成后,需要执行回调操作,就需要使用到 ChannelFuture 接口。

//connect是异步的,仅提交异步任务
ChannelFuture future= bootstrap.connect(new InetSocketAddress("www.manning.com",80));

//connect的异步任务真正执行完成后,future回调监听器才会执行
future.addListener(new  ChannelFutureListener(){
    @Override
    public void operationComplete(ChannelFuture channelFuture) throws Exception {
        if(channelFuture.isSuccess()){
            System.out.println("Connection  established");
        }else{
            System.err.println("Connection  attempt failed");
            channelFuture.cause().printStackTrace();
        }
    }
});

GenericFutureListener接口

GenericFutureListener的父接口EventListener是一个空接口,没有任何的抽象方法,是一个仅仅具有标识作用的接口,一般会使用它的子接口,如ChannelFutureListener。

public interface GenericFutureListener<F extends Future<? >> extends  EventListener {
    //监听器的回调方法
    void operationComplete(F var1) throws Exception;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值