从最初开始学习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. 写在最后
本文所展示的代码仅用于学习,切勿用于项目中!!!
|