IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 日常调戏Java:方法的分步执行 -> 正文阅读

[Java知识库]日常调戏Java:方法的分步执行

从最初开始学习Java编程开始,就产生一个认知:Java方法的执行通常是一步到位的,不能被打断,除非异常、程序终止或线程调度。因此,通常如果需要两个过程交叉进行并通信的话,采用多线程+线程同步的方式来进行。然而,线程切换和同步的开销有时是不可忽视的,那么有没有其他方式来处理这个问题呢?比如说,过程的调用者让一个过程执行到一半停下来,接着去做其他事,然后再回过来继续执行没有执行完的过程。例如这个过程:

Integer test0(Integer i) {
    System.out.println("执行操作 1");
    //让步 停止执行
    System.out.println("执行操作 2");
    //让步 停止执行
    System.out.println("执行操作 3");
    return i + 1;
}

在这个过程里让每一个步骤执行完停下,让调用者去做其他事情。本文就给出一种简单的实现方式。

1. 示例

先来看一个例子,这里模拟test方法和main交叉执行的情况。

public class Demo {

    public static void main(String[] args) {
        InterruptableProcedure<Integer, Integer> procedure = InterruptableProcedure.create((scheduler) -> (i) -> test(scheduler, i));
        /* 也可以写成这个样子
        InterruptableProcedure<Integer, Integer> procedure = InterruptableProcedure.create((scheduler) -> (i) -> {
            System.out.println("执行操作 1");
            return scheduler.yield(() -> {
                System.out.println("执行操作 2");
                return scheduler.yield(() -> {
                    System.out.println("执行操作 3");
                    return i + 1;
                });
            });
        });
        */

        procedure.start(1);          //这里只传递参数,不执行
        System.out.println("喝杯水");
        procedure.goNext();          //执行下一步
        System.out.println("睡一觉");
        procedure.goNext();          //执行下一步
        System.out.println("玩一会");
        procedure.goNext();          //执行下一步
        if (procedure.isEnd()) {     //判断是否结束
            int r = procedure.getValue();//取出返回值
            System.out.println("结束:" + r);
        }
    }

    public static Integer test(InterruptableProcedure<Integer, Integer>.Scheduler scheduler, Integer i) {
        System.out.println("执行操作 1");
        return scheduler.yield(() -> {//引入第一个中断点
            System.out.println("执行操作 2");
            return scheduler.yield(() -> {//引入第二个中断点
                System.out.println("执行操作 3");
                return i + 1;
            });
        });
    }
}

程序的打印输出如下:

喝杯水
执行操作 1
睡一觉
执行操作 2
玩一会
执行操作 3
结束:2

这里的goNext方法是执行到下一个scheduler.yield为止,也可以用goEnd方法直接执行完整个过程:

procedure.start(1);
System.out.println("喝杯水");
procedure.goNext();
System.out.println("睡一觉");
int x = procedure.goEnd();
System.out.println("结束:" + x);

打印的结果如下:

喝杯水
执行操作 1
睡一觉
执行操作 2
执行操作 3
结束:2

2. API功能介绍

  • InterruptableProcedure.start方法:起始方法,用于传递参数,重置状态,不执行实际过程。
  • InterruptableProcedure.goNext方法:执行实际过程的下一步(也就是到下一个scheduler.yield为止),如果没有下一步就抛出异常。
  • InterruptableProcedure.goEnd方法:从当前状态一直执行,直到整个过程结束。
  • InterruptableProcedure.isEnd方法:判断过程是否执行完。
  • InterruptableProcedure.getValue方法:获取过程的返回值。
  • Scheduler.yield方法:从当前过程中让步,参数为剩余要执行的过程。

3. 相关类的实现

interface Fun<T, R> extends Function<InterruptableProcedure<T, R>.Scheduler, Function<T, R>> {

}

class InterruptableProcedure<T, R> {
    private static final Supplier<?> EMPTY_FUN = () -> {
        throw new IllegalStateException("This procedure is end!");
    };
    private final Fun<T, R> originFun;  //原始完整过程
    private final Scheduler scheduler = new Scheduler();
    private T arg;
    private R result;
    private Supplier<R> intermediateFun;//中间过程,也是下一步要执行的过程

    InterruptableProcedure(Fun<T, R> originFun) {
        this.originFun = originFun;
    }

    public static <T, R> InterruptableProcedure<T, R> create(Fun<T, R> o) {
        return new InterruptableProcedure<>(o);
    }

    public void start(T t) {
        arg = t;
        intermediateFun = null;
        result = null;
    }

    public void goNext() {
        if (intermediateFun == null) {
            result = originFun.apply(scheduler).apply(arg);
            if (intermediateFun == null) {//处理方法中没有调用scheduler.yield的情况
                intermediateFun = (Supplier<R>) EMPTY_FUN;
            }
        } else {
            Supplier<R> temp = intermediateFun;
            result = intermediateFun.get();
            if (temp == intermediateFun) {//如果相等,表示intermediateFun没有更新,意味着整个过程已经执行完,
                intermediateFun = (Supplier<R>) EMPTY_FUN;
            }
        }
    }

    public boolean isEnd() {
        return intermediateFun == EMPTY_FUN;
    }

    public R getValue() {
        return result;
    }

    public R goEnd() {
        while (!isEnd()) {
            goNext();
        }
        return result;
    }

    public class Scheduler {
        public R yield(Supplier<R> supplier) {
            intermediateFun = supplier;
            return null;
        }
    }
}

4. 写在最后

本文所展示的代码仅用于学习,切勿用于项目中!!!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-24 09:15:04  更:2022-04-24 09:19:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:41:35-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码