Trampolining:java開發人員的實用指南

banq發表於2016-11-17
Trampolining是每個java程式設計師應該知道的概念,它代表計算的兩個狀態之一,一個代表計算完成有結果,另外一個指向計算下一步reminder,有點類似java.util.Supplier 所做的。這就為實現遞迴計算提供了可能,無需使用堆疊Stack,也無需使用執行緒硬編碼實現函式的交替執行。

無需Stack的遞迴計算
Trampolining是一個具有兩個具體實現的介面,其中一個簡單地表示結果,另一個表示計算的下一階段。

透過將計算分解成單獨的步驟,我們獲得了很多好處。其中之一是遞迴計算而不需要堆疊 。 我們可以移動任何遞迴呼叫在呼叫方法之外執行,從而將遞迴轉換為迭代。

斐波納契生成:

import static com.aol.cyclops.control.Trampoline.done;
import static com.aol.cyclops.control.Trampoline.more;

public void fib() {
    for(int i=0;i<100_000;i++)
       System.out.println(fibonacci(i, 0l, 1l).get());
}

public Trampoline<Long> fibonacci(Integer count, Long a, Long b) {
     return  count==0 ?  done(a) : more(()->fibonacci (count - 1, b, a + b));
}
<p class="indent">

當呼叫Trampoline的return語句時,內部遍歷呼叫隨著核心例項返回是More就不斷增加,一旦返回例項是Done就停止,基本上,我們透過基於迴圈的遞迴轉換為迭代,關鍵的啟用機制是Trampoline.more是一個惰性操作。

無執行緒併發
Trampolines另一個好處是能夠在同一個執行緒上交錯執行兩個或多個函式。一個實現案例我們內部的cyclops-react,使用先進的並行流型別LazyFutureStream.,能讓資料消費者在沒有資料可用時切換為資料生產者。

消費者與生產者的協同
讓我們展示如何使用Trampolines建立協同例程co-routines,在同一個執行緒上執行的交錯函式。

在下面的示例中,我們有一個bog標準JDK佇列,填充倒入某個單值。 我們想讀取三個值,但是我們不想發生任何讀取失敗情況。 如果沒有資料可用,我們希望我們的閱讀者也就是消費者能夠處理這種情況併產生一些資料。

交織功能的最簡單的方法是簡單地(硬編碼)寫在一起。 這是簡單但不靈活,它會捆綁到一個特定的實現。我們可以傳入一個Trampolines,如果沒有存在資料,那麼它會讓資料使用者產生資料。 因為我們的程式碼庫足夠靈活,可以處理未來的其他策略(也許消費者應該休眠一段時間?或者當前執行緒不斷檢查新資料是否到達)。

Queue<String> data = QueueX.of("hello");

    public void consumerToProducer(){
        System.out.println(nextData(produceToConsume()));
        System.out.println(nextData(produceToConsume()));
        System.out.println(nextData(produceToConsume()));
      /*
         hello
         world
         world   
       */
    }
    private Trampoline<String> produceToConsume(){
        return Trampoline.more( ()->{
            produce();
            return done(consume());
        });
        
    }
    public String nextData(Trampoline<String> noDataHandler){
        return getData(noDataHandler).get();
    }
    private Trampoline<String> getData(Trampoline<String> noDataHandler){
        return data.size()>0 ? done(consume()) : noDataHandler;
    }
    private String consume(){
        return data.poll();
    }
   
    public void produce(){
        data.offer("world");
    }

<p class="indent">

當佇列為空時,將執行noDataHandler Trampoline,因此,新資料被新增到佇列以供消費者使用。

LazyFutureStream
我們的專案cyclops引入了FutureStream概念,這是每個資料點嵌入一個Future的資料流,戶端不直接操作Future,而是像普通一樣定義(高階)Stream操作,由庫包管理Futures的流水線和它們的並行執行。

此外可以將Trampolines構建入API:實現Eval、map / flatMap 尾遞迴、Maybe、模式匹配等功能。詳細見原文:

Trampolining: a practical guide for awesome Java D

相關文章