📄 ch04s02.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>2. 在Java上实现Continuation:基于stack法</title><link rel="stylesheet" href="html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.69.1"><link rel="start" href="index.html" title="Java网络程序员看Continuation"><link rel="up" href="ch04.html" title="Chapter 4. Continuation的实现"><link rel="prev" href="ch04s01.html" title="1. Continuation的常规实现方法"><link rel="next" href="ch04s03.html" title="3. 在Java上实现Continuation:基于heap法"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">2. 在Java上实现Continuation:基于stack法</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch04s01.html">Prev</a> </td><th width="60%" align="center">Chapter 4. Continuation的实现</th><td width="20%" align="right"> <a accesskey="n" href="ch04s03.html">Next</a></td></tr></table><hr></div><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e542"></a>2. 在Java上实现Continuation:基于stack法</h2></div></div></div><p>答案是肯定的。</p><p>首先我们介绍一个不完整的解法。如果我们放弃对回退键的支持,那么我们就不需要整个continuation,因为只要有coroutine就足够支持call/answer了。从某种意义上讲,call/answer比回退键的支持重要,因为我们普遍上已经接受了网络程序不能很好地支持回退这个事实。而如前文所说,coroutine可以用两个线程来模拟。所以,如果我们不介意用很多线程,我们可以就可以用普通的Java实现call/answer。当然,操作系统可能不会喜欢web server用成千上万个线程,但是,比方说做一般的企业内部网络,这个方法是很可行的,而且企业内部网络往往正是逻辑最复杂的网络程序。</p><p>(注意C# 2.0提供了对coroutine的直接支持,无须用多线程模拟。)</p><p>另一方面,如果要在Java上支持真正的continuation,但是又不能修改JVM,那么我们只能从Java源代码或bytecode上着手,用这两者实现的区别不算太大,主要不同在于Java bytecode支持goto命令,而Java则不支持。修改Java bytecode,这早已不是什么新技术了,在Java上实现很多别的新功能,如AOP等,也同样是采用修改bytecode的方法。我们下面介绍一个用源代码支持continuation的例子,同样的方法也可以用在bytecode上。</p><p>首先,我们注意到用基于heap的方法很不容易,因为JVM一定会维持一个stack。除非我们能修改JVM,否则我们没法把这个stack放到heap中去。OK,其实这里我撒了个谎,我们还是有办法用基于heap的方式,当然这并不容易。我们在本章最后会看到一个这样的例子。</p><p>我们现在的这个例子用的是比较容易的基于stack的方法。如果我们用基于stack的方法,那么我们需要能够复制stack。这在JVM中是不可能的,因为JVM基于安全理由不让我们察看stack的内容。啊?什么?别着急,其实这里我又撒了个慌。用一点小技巧我们就能复制和恢复stack。来看例子:</p><pre class="programlisting">public class DuplicateStack { public static void parent() { System.out.println("Parent: 1"); child(1); System.out.println("Parent: 2"); child(2); System.out.println("Parent: 3"); } public static void child(int i) { System.out.println("Child: i = " + i); if (i == 2) { // record stack // } System.out.println("Child exits."); } public static void main(String[] args) { parent(); }}</pre><p>上面的例子没有实现// record stack //操作,我们将在后面把这部分补上。在实现该操作之前,您一定能够看出此例子的输出为:</p><pre class="screen">Parent: 1Child: i = 1Child exits.Parent: 2Child: i = 2Child exits.Parent: 3</pre><p>下面我们的Java程序和上面的是等价的,但是它实现了// record stack //操作。为了展示这个功能,我们在main()里让parent()函数返回多次:</p><pre class="programlisting">import java.util.Stack;class RecordStackException extends RuntimeException { RuntimeStack rs; public RecordStackException(RuntimeStack rs) { this.rs = rs; } public RuntimeStack getRuntimeStack() { return rs; }}class RuntimeStack implements Cloneable { private Stack countStack = new Stack(); private Stack varStack = new Stack(); public void pushCount(int c) { countStack.push(new Integer(c)); } public int popCount() { return ((Integer) countStack.pop()).intValue(); } public void pushVar(Object v) { varStack.push(v); } public Object popVar() { return varStack.pop(); } /* convenience methods */ public void pushInt(int i) { pushVar(new Integer(i)); } public int popInt() { return ((Integer) popVar()).intValue(); } public Object clone() { RuntimeStack s = new RuntimeStack(); s.countStack = (Stack) countStack.clone(); s.varStack = (Stack) varStack.clone(); return s; }}public class DuplicateStack { static void parentRecordStack(RuntimeStack s, int c) { s.pushCount(c); throw new RecordStackException(s); } static void parent(RuntimeStack s) { int count = 0; if (s != null) { count = s.popCount();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -