回调方法Callbak方法的理解——Java中回调的实现方式 & 从系统调用角度理解回调

在这里插入图片描述


回调方法就是一个参数,将一个A方法作为参数传到B方法里面,当B方法执行完之后,再执行传进去的这个A方法,这个过程就叫做回调。

回调方法是类A调用类B的方法,然后B又在某个时候反过来调用类B的方法a,这个a就是回调方法.

回调方法实现

回调方法就是一个参数,将一个A方法作为参数传到B方法里面,当B方法执行完之后,再执行传进去的这个A方法,这个过程就叫做回调。

回调方法是类A调用类B的方法,然后B又在某个时候反过来调用类B的方法a,这个a就是回调方法.

用反射实现

在这里插入图片描述

main方法

package com.tianju.callback;

public class Main {
    public static void main(String[] args) throws Exception{
        Request request = new Request();
        System.out.println("[Main]:我开了一个线程去异步发送请求");

        new Thread(()->{
            try {
                request.send(Callback.class, Callback.class.getMethod("processResponse"));
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();

        System.out.println("[Main]:请求发送完毕,do other things");
        Thread.sleep(10000);
    }
}

在这里插入图片描述

package com.tianju.callback;

import java.lang.reflect.Method;

public class Request {
    public void send(Class clazz, Method method) throws Exception{
        // wait for response
        Thread.sleep(3000); // 模拟等待响应
        System.out.println("[Response ]: 收到响应");
        method.invoke(clazz.newInstance());
    }
}

callback

package com.tianju.callback;

public class Callback {

    // callback details
    public void processResponse(){
        System.out.println("[callback]: 处理响应");
    }
}

直接调用callback

在这里插入图片描述

main

package com.tianju.dirctCall;

public class Main {
    public static void main(String[] args) throws Exception{
        Request request = new Request();
        System.out.println("[Main]:我开了一个线程去异步发送请求");

        Callback callback = new Callback();

        new Thread(()->{
            try {
                request.send(callback);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();


        System.out.println("[Main]:请求发送完毕,do other things");
        Thread.sleep(10000);
    }
}

request

package com.tianju.dirctCall;


public class Request {
    public void send(Callback callback) throws Exception{
        // wait for response
        Thread.sleep(3000); // 模拟等待响应
        System.out.println("[Response ]: 收到响应");
        callback.processResponse();
    }
}

进化:接口实现分离

在这里插入图片描述

匿名内部类 到 函数式编程

可以实现匿名内部类

在这里插入图片描述

package com.tianju.lamda;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Request request = new Request();
        System.out.println("[Main]:我开了一个线程去异步发送请求");
        new Thread(()->{
            try {
                request.send(new Callback() {
                    @Override
                    public void processResponse() {
                        System.out.println("[callback]: 处理响应");
                    }
                });
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();
        System.out.println("[Main]:请求发送完毕,do other things");
        Thread.sleep(10000);
    }
}

最终进化的形态,函数式编程

在这里插入图片描述

main

package com.tianju.lamda;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Request request = new Request();
        System.out.println("[Main]:我开了一个线程去异步发送请求");
        new Thread(()->{
            try {
                request.send(()->{
                    System.out.println("[Callback]: 处理响应");
                });
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }).start();
        System.out.println("[Main]:请求发送完毕,do other things");
        Thread.sleep(10000);
    }
}

从系统调用角度理解回调

应用系统模块之间的调用,通常分为:同步调用,异步调用,回调。

同步调用

在这里插入图片描述

同步调用是最基本的调用方式。类A的a()方法调用类B的b()方法,类A的方法需要等到B类的方法执行完成才会继续执行。如果B的方法长时间阻塞,就会导致A类方法无法正常执行下去。

异步调用

如果A调用B,B的执行时间比较长,那么就需要考虑进行异步处理,使得B的执行不影响A。通常在A中新起一个线程用来调用B,然后A中的代码继续执行。

异步通常分两种情况:第一,不需要调用结果,直接调用即可,比如发送消息通知;第二,需要异步调用结果,在Java中可使用Future+Callable实现。

不需要对方结果

在这里插入图片描述

需要对方的结果

回调属于一种双向的调用方式。回调的基本上思路是:A调用B,B处理完之后再调用A提供的回调方法(通常为callbakc())通知结果。

在这里插入图片描述

菜鸡问大佬问题案例

同步回调

在这里插入图片描述

package com.tianju.sycCallback;

public interface Callback {
    void callback(String str);
}
package com.tianju.sycCallback;

public class Person implements Callback{

    // @Autowired
    private Genius genius = new Genius();


    @Override
    public void callback(String str) {
        System.out.println("收到了答案:"+str);
    }

    public void ask(){
        System.out.println("菜鸡向大佬请教问题:");
        genius.answer(this);
    }

    public static void main(String[] args) {
        Person person = new Person();
        person.ask();
    }
}

在这里插入图片描述

package com.tianju.sycCallback;

public class Genius {

    public void answer(Callback callback){
        System.out.println("正在忙别的事情。。。。");
        try {
            Thread.sleep(2000);
            System.out.println("忙完了别的事情,开始思考问题。。。。。");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        System.out.println("思考出了问题的答案!结果为:XXXX");
        callback.callback("XXXX");
    }
}

在这里插入图片描述

在这里插入图片描述

异步回调

在这里插入图片描述

在这里插入图片描述

package com.tianju.asyCallback;

public class Person implements Callback {

    // @Autowired
    private Genius genius = new Genius();


    @Override
    public void callback(String str) {
        System.out.println("收到了答案:"+str);
    }

    public void askSyn(){
        System.out.println("菜鸡向大佬请教问题:");
        System.out.println("大佬收知道了。。。。");
        new Thread(()->{
            genius.answer(this);
        }).start();
        System.out.println("菜鸡回到自己的座位");

    }

    public static void main(String[] args) {
        Person person = new Person();
        person.askSyn();
    }
}

基于Future的半同步

在Java使用nio后无法立即拿到真实的数据,而是先得到一个"future",可以理解为邮戳或快递单,为了获悉真正的数据我们需要不停的通过快递单号"future"查询快递是否真正寄到。

Futures是一个抽象的概念,它表示一个值,在某一点会变得可用。一个Future要么获得计算完的结果,要么获得计算失败后的异常。

通常什么时候会用到Future呢?一般来说,当执行一个耗时的任务时,使用Future就可以让线程暂时去处理其他的任务,等长任务执行完毕再返回其结果。

经常会使用到Future的场景有:1. 计算密集场景。2. 处理大数据量。3. 远程方法调用等。

Java在 java.util.concurrent包中附带了Future接口,它使用Executor异步执行。

例如下面的代码,每传递一个Runnable对象到ExecutorService.submit()方法就会得到一个回调的Future,使用它检测是否执行,这种方法可以是同步等待线处理结果完成

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.tianju.future;

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

public class TestFuture {

    public static void main(String[] args) {

        //实现一个Callable接口
        Callable<User> c = () -> {
            //这里是业务逻辑处理
            System.out.println("开始处理业务了");

            //让当前线程阻塞1秒看下效果
            Thread.sleep(2000);
            return new User("张三");
        };

        ExecutorService es = Executors.newFixedThreadPool(2);

        // 记得要用submit,执行Callable对象
        Future<User> future = es.submit(c);
        // 一定要调用这个方法,不然executorService.isTerminated()永远不为true
        // 在此关闭中执行以前提交的任务,但不接受任何新任务。把当前任务执行完
        es.shutdown();
        // 无限循环等待任务处理完毕  如果已经处理完毕 isDone返回true
        while (!future.isDone()) {
            System.out.println("I am try");
            try {
                //处理完毕后返回的结果
                User nt = future.get();
                System.out.println("处理完结果了。。。。");
                System.out.println(nt.name);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    static class User {
        private String name;

        private User(String name) {
            this.name = name;
        }
    }
}

参考文章:https://zhuanlan.zhihu.com/p/349951124

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arya's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值