JDK9 Reactive Stream

1、概念

基於發佈訂閱者模式處理規範(機制),在JDK中真正叫法是FlowAPI

2、背壓(backpress)

  • 背壓指的發佈者和訂閱者之間的互動
  • 訂閱者可以告訴發佈者自己需要多少數據,可以調節數據流量,不會導致發佈者發佈數據過多導致數據浪費或壓垮訂閱者

3、Reactive Stream主要接口

java.util.concurrent.Flow類中定義的接口:

1)、Publisher<T>(發佈者)

subscribe(Subscriber<? super T> subscriber):保證發佈者和訂閱者之間通過此方法建立訂閱關係

2)、Subscriber<T>(訂閱者)

onSubscribe(Subscription subscription):第一此簽署訂閱關係,輸入就是Subscription對象

onNext(T item):接收到一條數據

onError(Throwable throwable):數據出錯

onComplete():數據處理完了

3)、Subscription(發佈者與訂閱者之間的關係)

request(long n):告訴發佈者需要多少資源

cancel():告訴發佈者不再接收數據了

4)、Processor<T,R> extends Subscriber<T>, Publisher<R>(既可以做消費者,又可以做發佈者,承擔中間角色)

4、案例

public class FlowDemo {
    public static void main(String[] args) throws InterruptedException {
        SubmissionPublisher<Integer> publisher = null;
        try {
            //定義發佈者,發佈的數據類型是Integer
            //直接使用jdk自帶的SubmissionPublisher,它實現了Publisher接口
            publisher = new SubmissionPublisher<>();

            //定義訂閱者
            Flow.Subscriber<Integer> subscriber = new Flow.Subscriber<>() {
                private Flow.Subscription subscription;

                @Override
                public void onSubscribe(Flow.Subscription subscription) {
                    //保存訂閱關係,需要用它來給發佈者響應
                    this.subscription = subscription;
                    //請求一個數據
                    this.subscription.request(1);
                }

                @Override
                public void onNext(Integer item) {
                    //接收一個數據並處理
                    System.out.println("接收到數據:" + item);
                    //處理完調用request再請求一個數據
                    this.subscription.request(1);
                    //或者 已經達到了目標,調用cancel告訴發佈者不再接受數據了
                    //this.subscription.cancel();
                }

                @Override
                public void onError(Throwable throwable) {
                    //出現了異常(例如處理數據的時候產生了異常)
                    throwable.printStackTrace();
                    //告訴發佈者,後面不接收數據了
                    this.subscription.cancel();
                }

                @Override
                public void onComplete() {
                    //全部數據處理完了(發佈者關閉了)
                    System.out.println("處理完了!");
                }
            };

            //發佈者和訂閱者之間建立訂閱關係
            publisher.subscribe(subscriber);

            //發佈數據
            publisher.submit(100);
            publisher.submit(101);
            publisher.submit(102);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (publisher != null) {
                //關閉發佈者
                publisher.close();
            }
        }
        TimeUnit.SECONDS.sleep(1);
    }
}

添加Processor中轉站實例代碼

/**
 * Processor需要繼承SubmissionPublisher並實現Processor接口
 * 輸入數據Integer,過濾小於0的,然後轉換成字符串發佈出去
 */
class MyProcessor extends SubmissionPublisher<String> implements Flow.Processor<Integer, String> {
    private Flow.Subscription subscription;

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        //保存訂閱關係,需要用它來給發佈者響應
        this.subscription = subscription;
        //請求一個數據
        this.subscription.request(1);
    }

    @Override
    public void onNext(Integer item) {
        //接收一個數據並處理
        System.out.println("處理器接收到數據:" + item);
        //過濾掉小於0
        if (item > 0) {
            this.submit("轉換後的數據:" + item);
        }
        //處理完調用request再請求一個數據
        this.subscription.request(1);
    }

    @Override
    public void onError(Throwable throwable) {
        //出現了異常(例如處理數據的時候產生了異常)
        throwable.printStackTrace();
        //告訴發佈者,後面不接收數據了
        this.subscription.cancel();
    }

    @Override
    public void onComplete() {
        //全部數據處理完了(發佈者關閉了)
        System.out.println("處理器處理完了!");
        //關閉發佈者
        this.close();
    }
}

public class FlowDemo2 {
    public static void main(String[] args) throws InterruptedException {
        SubmissionPublisher<Integer> publisher = null;
        try {
            //定義發佈者,發佈的數據類型是Integer
            //直接使用jdk自帶的SubmissionPublisher,它實現了Publisher接口
            publisher = new SubmissionPublisher<>();

            //定義處理器,對數據進行過濾
            MyProcessor processor = new MyProcessor();

            //定義訂閱者
            Flow.Subscriber<String> subscriber = new Flow.Subscriber<>() {
                private Flow.Subscription subscription;

                @Override
                public void onSubscribe(Flow.Subscription subscription) {
                    //保存訂閱關係,需要用它來給發佈者響應
                    this.subscription = subscription;
                    //請求一個數據
                    this.subscription.request(1);
                }

                @Override
                public void onNext(String item) {
                    //接收一個數據並處理
                    System.out.println("接收到數據:" + item);
                    //處理完調用request再請求一個數據
                    this.subscription.request(1);
                    //或者 已經達到了目標,調用cancel告訴發佈者不再接受數據了
                    //this.subscription.cancel();
                }

                @Override
                public void onError(Throwable throwable) {
                    //出現了異常(例如處理數據的時候產生了異常)
                    throwable.printStackTrace();
                    //告訴發佈者,後面不接收數據了
                    this.subscription.cancel();
                }

                @Override
                public void onComplete() {
                    //全部數據處理完了(發佈者關閉了)
                    System.out.println("處理完了!");
                }
            };

            //發佈者和處理器建立訂閱關係
            publisher.subscribe(processor);
            //處理器和最終訂閱者建立訂閱關係
            processor.subscribe(subscriber);

            //發佈數據
            publisher.submit(100);
            publisher.submit(-100);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (publisher != null) {
                //關閉發佈者
                publisher.close();
            }
        }
        TimeUnit.SECONDS.sleep(1);
    }
}

執行結果:

處理器接收到數據:100
處理器接收到數據:-100
處理器處理完了!
接收到數據:轉換後的數據:100
處理完了!

5、實現原理

在這裏插入圖片描述

在這裏插入圖片描述

發佈者發佈的數據先存儲在BufferedSubscription的array數組(相當於緩衝池,默認長度256)中,訂閱者請求一個數據實際上請求的是存儲在BufferedSubscription的array數組中的數據

發佈者的submit(T item)方法是一個阻塞方法,如果BufferedSubscription的array數組滿了,submit(T item)方法就會被阻塞,直到array數組的數據被訂閱者消費,從而就可以調節生產者生產數據的速度

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章