并发基础-Future异步回调模式

并发基础中的Future异步回调模式

背景

随着业务模块系统越来越多,各个系统的业务架构变得越来越复杂,跨设别的接口调用越来越复杂,因此,面临一个问题: 如何高效率的异步去调用这些接口,然后同步去处理接口的返回结果? 涉及线程的异步回调问题,这也是高并发的一个基础问题

泡茶案例

join异步阻塞

使用join实现异步泡茶喝的实践案例
package com.wangyg.FutureTask.Join;

import util.Logger;

/**
 * 程序中有三个线程:
 * 主线程 main
 * 烧水线程hThread
 * 清洗线程 wThread
 *
 */
public class JoinDemo {
    public static final int SLEEP_GAP=500;

    /**
     * 获取当前线程的名字
     * @return
     */
    public static  String getCurThreadName(){
        return Thread.currentThread().getName();
    }

    /**
     * 烧水线程
     */
    static  class HotWaterThread extends  Thread{
        //烧水线程
        public HotWaterThread() {
            super("** 烧水-Thread");
        }
        public void run(){
            try {
                Logger.info("洗好水壶");
                Logger.info("灌上凉水");
                Logger.info("放在火上");
                //线程睡眠一段时间,表示烧水中
                Thread.sleep(SLEEP_GAP);
                Logger.info("水开了");
            } catch (InterruptedException e) {
                e.printStackTrace();
                Logger.info("发生异常被中断");
            }
            Logger.info("运行结束");
        }
    }

    static  class WashThread extends  Thread{
        public WashThread() {
            super("$$ 清洗-Thread");
        }
        public  void run(){
            try {
                Logger.info("洗茶壶");
                Logger.info("洗茶杯");
                Logger.info("拿茶叶");
                //线程睡眠一段时间,代表清洗中
                Thread.sleep(SLEEP_GAP);
                Logger.info("洗完了");
            } catch (InterruptedException e) {
                Logger.info("发生异常 被中断");
            }
            Logger.info("运行结束.");
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        Thread hThread= new HotWaterThread(); //烧水线程
        Thread wThread = new WashThread(); //洗水杯线程

        hThread.start();
        wThread.start();
        try {
            // 合并烧水-线程
            hThread.join(); //使用join进行加塞线程
            // 合并清洗-线程
            wThread.join(); //使用join进行加塞线程

            Thread.currentThread().setName("主线程");
            Logger.info("泡茶喝");

        } catch (InterruptedException e) {
            Logger.info(getCurThreadName() + "发生异常被中断.");
        }
        Logger.info(getCurThreadName() + " 运行结束.");
        long end = System.currentTimeMillis() - start;
        System.out.println(end);
    }
}

join合并方法

应用场景

A线程调用B线程的join方法,等待B线程执行完成,A线程阻塞

  • join是实例方法,不是静态方法,需要使用线程对象调用,thread.join()
  • join 调用时,当前线程阻塞

FutureTask异步回调

Java1.5版本后,提供一个新的多线程创建方式–FutureTask方式

Callable接口
Callable代码
public interface Callable<V>{
//call方法有返回值	
V call() throws Exception;
}
  • 有返回值
  • 可以抛出异常
  • 泛型接口
FutureTask类

FutureTask类代表一个未来执行的任务,表示新线程所执行的操作,FutureTask类位于 java.util.concurrent包中

FutureTask构造函数参数

Callable类型, 实际上是对Callable类型的二次封装

Future接口

Future接口并不复杂,主要是对并发任务的执行及获取结果的一些操作,主要提供三个功能:

  1. 判断并发任务是否执行完成
  2. 获取并发的任务的结果
  3. 取消并发执行中的任务
使用FutureTask类实现异步泡茶喝的实践案例
package com.wangyg.netty.ch05;

import util.Logger;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class JavaFutureDemo {
    public static  final int SLEEP_GAP=500;

    public static  String getCurrentThreadName(){
        return Thread.currentThread().getName();
    }

    static  class HotWaterJob implements Callable<Boolean>{

        //重写call方法
        @Override
        public Boolean call() throws Exception {
            try {
                Logger.info("洗好水壶");
                Logger.info("灌上凉水");
                Logger.info("放在火上");
                Thread.sleep(SLEEP_GAP);
                Logger.info("水开了");
            } catch (Exception e) {
                Logger.info("发生异常被中断。");
                return false;
            }
            Logger.info("运行结束.");
            return true;
        }
    }

    static  class WashJob implements  Callable<Boolean>{
        //重写call()方法
        @Override
        public Boolean call() throws Exception {
            try {
                Logger.info("洗茶壶");
                Logger.info("洗茶杯");
                Logger.info("拿茶叶");
                Thread.sleep(SLEEP_GAP);
                Logger.info("洗完了");
            } catch (Exception e) {
                Logger.info("清洗工作发生中断异常.");
                return false;
            }
            Logger.info("清洗工作运行结束。");
            return true;
        }
    }


    public static void drinkTea(Boolean waterOk, boolean cupOk) {//两个参数:是否烧水成功,是否洗杯子成功
        if(waterOk && cupOk){
            Logger.info("泡茶喝");
        }else if(!waterOk){
            Logger.info("烧水失败, 没有茶喝了");
        }else if(!cupOk){
            Logger.info("杯子洗不了,没有茶喝了");
        }

    }

    //main函数
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        Callable<Boolean> hJob = new HotWaterJob();
        //传入callable接口
        FutureTask<Boolean> hTask = new FutureTask<>(hJob);
        //在使用Thread  传入 runnable线程接口
        Thread hThread = new Thread(hTask, "** 烧水-Thread");

        Callable<Boolean> wJob = new WashJob();
        //使用futureTask包装
        FutureTask<Boolean> wTask = new FutureTask<>(wJob);
        Thread wThread = new Thread(wTask, "$$ 清洗-Thread");
        hThread.start();
        wThread.start();

        Thread.currentThread().setName("主线程");
        try {
            boolean hOk = hTask.get(); //获取异步线程执行结果
            boolean wOk = wTask.get();//获取清洗线程异步执行的结果
        } catch (Exception e) {
            e.printStackTrace();
            Logger.info(getCurrentThreadName()+"发生异常被中断.");
        }
        Logger.info(getCurrentThreadName() + " 运行结束.");
        long end = System.currentTimeMillis();
        System.out.println(end-start);
    }
}

结论

目前这两种方式效率是差不多的,因为FutureTask类的get方法,获取异步结果时,主线程也会阻塞的,前两种方式(join,FutureTask) 都是异步阻塞模式

Guava的异步回调

谷歌公司提供的Java扩展包,提供了一种异步回调的解决方案,相关源码在com.google.common.util.concurrent包中

Guava对java的异步回调机制,做了一下增强:

引入一个新的接口ListennableFuture,继承Java的future接口,是的java的Future异步任务,在Guava中能被监控和获取非阻塞异步执行的结果

引入一个新的接口FutureCallback,这是一个独立的新接口,该接口的目的,是在异步任务执行完成后,根据异步结果,完成不同的回调任务,并且可以处理异步结果

FutureCallback介绍

FutureCallback有用两个回调方法

  • onSuccess方法,在异步执行成功后被回调
  • onFailure方法,异步任务执行过程中,抛出异常被执行
使用Guava实现泡茶喝的实践案例
package com.wangyg.netty.ch05;


import com.google.common.util.concurrent.*;
import util.Logger;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by 尼恩 at 疯狂创客圈
 */

public class GuavaFutureDemo {

    public static final int SLEEP_GAP = 500;


    public static String getCurThreadName() {
        return Thread.currentThread().getName();
    }

    static class HotWarterJob implements Callable<Boolean> {

        @Override
        public Boolean call() throws Exception {

            try {
                Logger.info("洗好水壶");
                Logger.info("灌上凉水");
                Logger.info("放在火上");

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

            } catch (InterruptedException e) {
                Logger.info(" 发生异常被中断.");
                return false;
            }
            Logger.info(" 烧水工作,运行结束.");

            return true;
        }
    }

    static class WashJob implements Callable<Boolean> {

        @Override
        public Boolean call() throws Exception {
            try {
                Logger.info("洗茶壶");
                Logger.info("洗茶杯");
                Logger.info("拿茶叶");
                //线程睡眠一段时间,代表清洗中
                Thread.sleep(SLEEP_GAP);
                Logger.info("洗完了");

            } catch (InterruptedException e) {
                Logger.info(" 清洗工作 发生异常被中断.");
                return false;
            }
            Logger.info(" 清洗工作  运行结束.");
            return true;
        }

    }

    //泡茶线程
    static class MainJob implements Runnable {

        boolean warterOk = false;
        boolean cupOk = false;
        int gap = SLEEP_GAP / 10;

        @Override
        public void run() {
            boolean flg =true;
            while (flg) {
                try {
                    Thread.sleep(gap);
                    Logger.info("读书中......");
                } catch (InterruptedException e) {
                    Logger.info(getCurThreadName() + "发生异常被中断.");
                }

                if (warterOk && cupOk) {
                    drinkTea(warterOk, cupOk);
                    flg=false;
                }
            }
        }


        public void drinkTea(Boolean wOk, Boolean cOK) {
            if (wOk && cOK) {
                Logger.info("泡茶喝,茶喝完");
                this.warterOk = false;
                this.gap = SLEEP_GAP * 100;
            } else if (!wOk) {
                Logger.info("烧水失败,没有茶喝了");
            } else if (!cOK) {
                Logger.info("杯子洗不了,没有茶喝了");
            }

        }
    }

    public static void main(String args[]) {

        //新起一个线程,作为泡茶主线程
        MainJob mainJob = new MainJob();
        Thread mainThread = new Thread(mainJob);
        mainThread.setName("主线程");
        mainThread.start();

        //烧水的业务逻辑
        Callable<Boolean> hotJob = new HotWarterJob();
        //清洗的业务逻辑
        Callable<Boolean> washJob = new WashJob();

        //创建java 线程池
        ExecutorService jPool =
                Executors.newFixedThreadPool(10);

        //包装java线程池,构造guava 线程池
        ListeningExecutorService gPool =
                MoreExecutors.listeningDecorator(jPool);

        //提交烧水的业务逻辑,取到异步任务
        ListenableFuture<Boolean> hotFuture = gPool.submit(hotJob);
        //绑定任务执行完成后的回调,到异步任务
        Futures.addCallback(hotFuture, new FutureCallback<Boolean>() {
            public void onSuccess(Boolean r) {
                if (r) {
                    mainJob.warterOk = true;
                }
            }

            public void onFailure(Throwable t) {
                Logger.info("烧水失败,没有茶喝了");
            }
        });
        //提交清洗的业务逻辑,取到异步任务

        ListenableFuture<Boolean> washFuture = gPool.submit(washJob);
        //绑定任务执行完成后的回调,到异步任务
        Futures.addCallback(washFuture, new FutureCallback<Boolean>() {
            public void onSuccess(Boolean r) {
                if (r) {
                    mainJob.cupOk = true;
                }
            }
            public void onFailure(Throwable t) {
                Logger.info("杯子洗不了,没有茶喝了");
            }
        });
    }


}

Netty 的异步回调模式

Netty和Guava一样,实现了自己的异步回调体系: Netty继承和扩展了JDK Future系列异步回调 API,定义了自身的Future系列接口和类,实现了异步任务的监控,异步执行结果的获取

总体来说,Netty对javaFuture异步任务的扩展如下:

  1. 继承了java的Future接口,得到一个新的属于Netty自己的Future异步任务接口,该接口对原有的接口进行了增强,是的netty异步任务能够以非阻塞的方式处理回调的结果
  2. 引入一个新接口–GennericFutureListener, 用于表示异步执行完成的监听器,这个接口和Guava的FutureCallback回调接口不同,netty使用了监听器的模式,异步任务的执行完成后的回调逻辑抽象成了Listener监听器接口
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值