新日撸java三百行` 新手小白java学习记录 `Day1

新日撸java三百行新手小白java学习记录

Day1 模拟多线程回调机制


文章目录

  • [新日撸java三百行` 新手小白java学习记录 `](#新日撸java三百行新手小白java学习记录)
  • 前言
  • [一 、模拟异步机制](#一 、模拟异步机制)
  • 提出问题
  • 解决方案

前言

古人称长江为江,黄河为河。长江水清,黄河水浊,长江在流,黄河也在流。 长江之水灌溉了两岸数省之田地,黄河之水也灌溉了数省两岸之田地,只能不因水清而偏用,也只能不因水浊而偏废,自古皆然。


一 、模拟异步机制

提出问题

众所周知,java实现多线程可以实现Runnable、Callable两个接口,两者区别在于有无返回值。

目标:探究Callable的返回值如何返回的

问题:主线程中派生一个线程A执行有返回值的任务,如何解决主子线程时间差。

解决方案

使用一个已知的对象来承接返回值,命名为MyCompletableFuture。

  • 1.定义自已的任务实现接口myable类似Callable接口
java 复制代码
public interface Myable<T> {
    T my();
}
  • 2.创建MyCompletableFuture类
    核心类,目的是截断子线程的返回值,使用该类代替子线程的返回值。
    大概逻辑:持有任务接口(符合里式替换原则),持有任务的返回值result, 持有标志位isComplete判断任务是否完成,持有还有一个自定义回调接口CallBack。
    -- get()方法,调用该方法的线程会尝试获取任务的结果,此时有两种情况:任务完成和未完成。
    -- submit()方法开启一个线程,调用任务接口的my方法执行任务并将返回值赋值给result,完成后调用Complete()方法
    -- Complete()方法将标志位变成true标明任务完成;唤醒等待中的线程;调用回调。
java 复制代码
public class MyCompletableFuture<T> {

    private Myable<T> myable;
    private T result;
    private Exception exception;

    private boolean isComplete = false;

    // private List<Function<T , ?>> callbacks = new ArrayList();

    private List<CallBack<T , ?>> callbacks = new ArrayList();
    public MyCompletableFuture() {
    }

    public MyCompletableFuture(Myable<T> myable) {
        this.myable = myable;
    }

    /**
     * 线程的启动方法 , 运行一个Myable的实现类 , 并接受返回值复制给当前类属性 , 而后调用Complete()方法。
     */
    public void submit(){
        new Thread(
                ()->{
                    this.result = myable.my();
                    try {
                        Complete();
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
        ).start();
    }

    /**
     * 当此方法被调用,说明返回值已经被成功接收 , 届时可以调整状态属性 , 唤醒调用get()的其他线程 , 执行回调。
     * @throws Exception
     */
    private synchronized void  Complete() throws Exception {

        // this.result = result;
        isComplete = true;
        notifyAll();
        executeCallbacks();
    }

    public synchronized void completeExceptionally (Exception e){
        this.exception = e;
        this.isComplete = true;
        notifyAll();
        executeCallbacks();
    }

    /**
     * 判断返回值是否就绪 , 如果就绪则调用列表中的订阅者的回调函数 , 否则订阅该列表。
     * @param callback : 实现CallBack接口的回调方法
     * @return
     */
    public synchronized MyCompletableFuture<Void> thenApply(CallBack<T , ?> callback){
        if(isComplete){
            if(exception != null){
                throw new RuntimeException(exception);
            }
            callback.callBack(result);
        }
        else {
            callbacks.add(callback);
        }
        return new MyCompletableFuture<>();
    }


    /**
     * 发布订阅模式执行回调。
     */
    public synchronized void executeCallbacks(){
        for (CallBack<T, ?> callback: callbacks
             ) {
            callback.callBack(result);
        }

        callbacks.clear();
    }


    /**
     * 获取返回值的方法,如果准备就绪则获取,否则wait();
     * @return
     * @throws Exception
     */
    public synchronized T get() throws  Exception{
        if(!isComplete){
            wait();
        }

        if(exception != null){
            throw exception;
        }
        return result;
    }
}
  • 3.回调实现逻辑
    -- 定义回调接口CallBack(函数式编程接口)
java 复制代码
public interface CallBack<T , R> {
    R callBack(T t);
}

-- 使用发布订阅模式在MyCompletableFuture中持有了一个CallBack类型的List,实现一个注册方法thenApply,当需要子线程结果的时候就调用thenApply方法

  • 4.使用案例
    定义任务类实现任务接口,任务就是对一个数组排序
java 复制代码
class AsyncProcessor implements Myable<int[]>{

    int nums[] ;

    public AsyncProcessor(int[] nums) {
        this.nums = nums;
    }

    @Override
    public int[] my() {
        try {
            Thread.sleep(1000);
            System.out.println("耗时排序中.......");
            Thread.sleep(2000);


            Arrays.sort(nums);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return nums;
    }
}

主函数 将任务交给MyCompletableFuture

java 复制代码
public class Main {
    public static void main(String[] args) throws Exception {
        int nums[] = new int[]{1,5,7,8,1,5,78,1,5,1};
        AsyncProcessor processor = new AsyncProcessor(nums);


        MyCompletableFuture<int[]> futureResult = new MyCompletableFuture<>(processor);

        futureResult.submit();

        futureResult.thenApply(result -> {
            System.out.println("最大值: " + result[nums.length-1]);
            return null; // 返回值为 void
        });

        futureResult.thenApply(result -> {
            System.out.println("最小值: " + result[0]);
            return null; // 返回值为 void
        });


        futureResult.thenApply(result -> {
            System.out.println("排序结果: " + Arrays.toString(result));
            return null; // 返回值为 void
        });
        // 在主线程继续执行其他操作
        System.out.println("请求已发送,等待处理结果...");

        // 获取结果(会阻塞直到完成)
        try {
            futureResult.get(); // 获取处理结果,等待完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

总结:主线程想要不阻塞继续执行,必然会有一个已知的类型对象来接受返回值,进而将真正的返回值隐藏在该对象中。

相关推荐
向阳2566 分钟前
SpringBoot+vue前后端分离整合sa-token(无cookie登录态 & 详细的登录流程)
java·vue.js·spring boot·后端·sa-token·springboot·登录流程
XiaoLeisj23 分钟前
【MyBatis】深入解析 MyBatis XML 开发:增删改查操作和方法命名规范、@Param 重命名参数、XML 返回自增主键方法
xml·java·数据库·spring boot·sql·intellij-idea·mybatis
风象南24 分钟前
SpringBoot实现数据库读写分离的3种方案
java·spring boot·后端
振鹏Dong30 分钟前
策略模式——本质是通过Context类来作为中心控制单元,对不同的策略进行调度分配。
java·策略模式
ChinaRainbowSea39 分钟前
3. RabbitMQ 的(Hello World) 和 RabbitMQ 的(Work Queues)工作队列
java·分布式·后端·rabbitmq·ruby·java-rabbitmq
雾月5540 分钟前
LeetCode 914 卡牌分组
java·开发语言·算法·leetcode·职场和发展
melck1 小时前
liunx日志查询常用命令总结
java·服务器·网络
守护者1701 小时前
JAVA学习-练习试用Java实现“实现一个Hadoop程序,使用Hive进行复杂查询和数据筛查”
java·学习
程序员 小柴1 小时前
docker的与使用
java·docker·eureka
ゞ 正在缓冲99%…1 小时前
leetcode76.最小覆盖子串
java·算法·leetcode·字符串·双指针·滑动窗口