Java回调实现异步 (转)

摘要:
回调和异步调用密切相关。我们通常使用回调来注册异步消息和异步调用来实现消息通知。多线程中的“回调”Java多线程可以通过可调用和未来或未来任务的组合来获得线程执行后的返回值。在publicclass多线程中的回调{//这里,我们简单地使用future和callable来实现publicstaticvoidmaintrowsExecutionException,InterruptedException{ExecutorServiceexecutor=Executors.newCachedThreadPool();future<String>future=executor.submit;//手动阻止调用get以通过调用方法获取返回值。

出处: Java回调实现异步

  在正常的业务中使用同步线程,如果服务器每处理一个请求,就创建一个线程的话,会对服务器的资源造成浪费。因为这些线程可能会浪费时间在等待网络传输,等待数据库连接等其他事情上,真正处理业务逻辑的时间很短很短,但是其他线程在线程池满了之后又会阻塞,等待前面的线程处理完成。而且,会出现一个奇怪的现象,客户端的请求被阻塞,但是cpu的资源使用却很低,大部分线程都浪费在处理其他事情上了。所以,这就导致服务器并发量不高。

  而异步,则可以解决这个问题。

  我们可以把需要用到cpu的业务处理使用异步来实现,这样其他请求就不会被阻塞,而且cpu会保持比较高的使用率。

  今天就学习了使用回调来实现异步的方法。我们设想一个情景,A是处理业务的一个步骤,A需要解决一个问题,这时候A可以问B,让B来告诉A答案,这期间,A可以继续做自己的事情,而不用因为B做的事而阻塞。于是,我们想到给B设置一个线程,让B去处理耗时的操作,然后处理完之后把结果告诉A。所以这个问题的要点就在于B处理完之后如何把结果告诉A。我们可以直接在A中写一个方法对B处理完的结果进行处理,然后B处理完之后调用A这个方法。这样A调用B去处理过程,B调用A的C方法去处理结果就叫做回调。

package CallBack;

public interface CallBack {
    /*
    *A处理结果的方法,为什么要写这个接口呢?
    *因为可能不止A需要用到B的处理过程,如果很多地方需要用到B
    * 那么传入B的方法就不可能只传A类,所以要定义一个接口,
    * 传入B的处理方法的参数就是这个接口对象
    * */
    public void solve(String result);
}
package CallBack;

public class A implements CallBack {
    private B b;

    public A(B b){
        this.b=b;
    }

    //A需要解决一个问题,所以他把问题交给B处理,B单独创建一个线程,不影响A的运行
    public void ask(final String question){
        System.out.println("A问了B一个问题");
        new Thread(()->{
            //B想要帮A处理东西,就必须知道谁让自己处理的,所以要传入a,也要知道a想处理什么,所以要传入question
            b.executeMessage(A.this,question);
        }).start();
        //A把要处理的事情交给b之后,就可以自己去玩耍了,或者去处理其他事情
        play();
    }

    public void play(){
        System.out.println("我要逛街去了");
    }

    //A拿到了B处理完成的结果,可以进行一些操作,比如把结果输出
    @Override
    public void solve(String result) {
        System.out.println("B告诉A的答案是--》"+result);
    }

}
package CallBack;

public class B {
    public void executeMessage(CallBack callBack,String question){
        System.out.println(callBack.getClass()+"问的问题--》"+question);
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String result="答案是2";
        callBack.solve(result);
    }
}
package CallBack;

public class test {
    public static void main(String[] args) {
        B b=new B();
        A a=new A(b);
        a.ask("1+1=?");
    }
}

console结果:

运行结果:
A问了B一个问题
我要逛街去了
class CallBack.A问的问题--》1+1=?
B告诉A的答案是--》答案是2

Process finished with exit code 0

异步回调的实现依赖于多线程或者多进程

  软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用回调异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知

多线程中的“回调” (JDK8之前)

  Java多线程中可以通过callable和future或futuretask结合来获取线程执行后的返回值。实现方法是通过get方法来调用callable的call方法获取返回值。

  其实这种方法本质上不是回调,回调要求的是任务完成以后被调用者主动回调调用者的接口。而这里是调用者主动使用get方法阻塞获取返回值。

一般情况下,我们会结合Callable和Future一起使用,通过ExecutorService的submit方法执行Callable,并返回Future。

public class 多线程中的回调 {
    //这里简单地使用future和callable实现了线程执行完后
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<String> future = executor.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("call");
                TimeUnit.SECONDS.sleep(1);
                return "str";
            }
        });
        //手动阻塞调用get通过call方法获得返回值。
        System.out.println(future.get());
        //需要手动关闭,不然线程池的线程会继续执行。
        executor.shutdown();

    //使用futuretask同时作为线程执行单元和数据请求单元。
    FutureTask<Integer> futureTask = new FutureTask(new Callable<Integer>() {
        @Override
        public Integer call() throws Exception {
            System.out.println("dasds");
            return new Random().nextInt();
        }
    });
    new Thread(futureTask).start();
    //阻塞获取返回值
    System.out.println(futureTask.get());
}
@Test
public void test () {
    Callable callable = new Callable() {
        @Override
        public Object call() throws Exception {
            return null;
        }
    };
    FutureTask futureTask = new FutureTask(callable);

}
}

  比起future.get(),其实更推荐使用get (long timeout, TimeUnit unit) 方法,设置了超时时间可以防止程序无限制的等待future的结果。

CompletableFuture介绍(JDK8)

Future模式的缺点

  • Future虽然可以实现获取异步执行结果的需求,但是它没有提供通知的机制,我们无法得知Future什么时候完成。

  • 要么使用阻塞,在future.get()的地方等待future返回的结果,这时又变成同步操作。要么使用isDone()轮询地判断Future是否完成,这样会耗费CPU的资源。

CompletableFuture

  Netty、Guava分别扩展了Java 的 Future 接口,方便异步编程。

  Java 8新增的CompletableFuture类正是吸收了所有Google Guava中ListenableFuture和SettableFuture的特征,还提供了其它强大的功能,让Java拥有了完整的非阻塞编程模型:Future、Promise 和 Callback(在Java8之前,只有无Callback 的Future)。

  CompletableFuture能够将回调放到与任务不同的线程中执行,也能将回调作为继续执行的同步函数,在与任务相同的线程中执行。它避免了传统回调最大的问题,那就是能够将控制流分离到不同的事件处理器中。

  CompletableFuture弥补了Future模式的缺点。在异步的任务完成后,需要用其结果继续操作时,无需等待。可以直接通过thenAccept、thenApply、thenCompose等方式将前面异步处理的结果交给另外一个异步事件处理线程来处理。

相关详细API内容介绍: 理解Java8里面CompletableFuture异步编程

免责声明:文章转载自《Java回调实现异步 (转)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇junit 常用注解 + junit 断言详解Jmeter核心-慧测课堂笔记下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

设计模式七大原则——接口隔离原则

一、基本介绍   一个类对另一个类的依赖应该建立在最小的接口上 二、应用实例    分析以上类图:   首先有四个类,分别是A,B,C,D,以及一个接口Interface1,其中有五个抽象方法,分别是operation1,operation2,operation3,operation4,operation5   类B和类D都会实现Interface1接口...

利用C#线程窗口调试多线程程序

       从网上的资料判断,调试多线程程序似乎就一下3种方法。 1、在日志的某个地方写日志文件。 优点:不会干扰程序的执行,特别是对网络的多线程通信。 缺点:每次都需要打开日志文件以查看进程运行的信息。 2、利用断点进行调试。 优点:直观,可以直接看到运行过程的值 缺点:在多个线程设置断点,可能让程序跳来跳去,还需要额外地分出一部分精力用来理清程序...

Java(多)线程中注入Spring的Bean

问题说明 今天在web应用中用到了Java多线程的技术来并发处理一些业务,但在执行时一直会报NullPointerException的错误,问题定位了一下发现是线程中的Spring bean没有被注入,bean对象的值为null。 原因分析 web容器在启动应用时,并没有提前将线程中的bean注入(在线程启动前,web容易也是无法感知的) 解决方案 方法有...

CC2540开发板学习笔记(九)—— BLE协议简介

一、BLE协议简介 1、协议是什么?      协议是一系列的通信标准,双方需要共同按照这进行正常数据 协议是一系列的通信标准,双方需要共同按照这进行正常数据发射和 接收。协议栈是的具体实现形式,通俗点来理解就发射和 接收。协议栈是的具体实现形式,通俗点来理解就发射和 接收。协议栈是的具体实现形式,通俗点来理解就用户之间的一个接口,开发人员通过使协议栈来这...

通过 Javacore 了解线程运行状况

Javacore 是一个当前 JVM 运行状态的快照。通过对Javacore 的分析,可以了解在 JVM 中运行的应用程序的当前状态,比如是否“卡”在某一点上,或在某些代码上运行时间太长。 Javacore 的基本内容 Javacore,也可以称为“threaddump”或是“javadump”,它是 Java 提供的一种诊断特性,能够提供一份可读的当前运...

使用 async-await 简化代码的检讨

  从API版本升级到4.6之后, Unity支持了async和await语法, 并且根据测试来看, 它运行在主线程里, 跟一般的C#编译不大一样, 这就很有操作空间了, 先来看看普通C# Console工程和Unity中运行的差别:   1. C# Console using System; namespace AsyncTest { clas...