web-dev-qa-db-ja.com

Java 9-パブリッシャーとサブスクライバーのしくみ

Java 9SubscriberPublisherがどのように機能するかを理解しようとしています。

ここで1つのsubscriberを作成し、アイテムの公開にSubmissionPublisherを使用しています。

100個の文字列をsubscriberに公開しようとしています。 Clientプログラムをsleepにしないと(MyReactiveAppのコメント付きコードを参照)、すべてのアイテムが公開されているとは限りません。

ここで処理されるすべての文字列を待機しないのはなぜですか。

strs.stream().forEach(i -> publisher.submit(i)); // what happens here? 

上記のコードをに置き換えると、すべての文字列がコンソールに出力されます

strs.stream().forEach(System.out::println);

SubmissionPublisherを使用して公開するクライアントプログラム。

import Java.util.List;
import Java.util.concurrent.SubmissionPublisher;
import Java.util.function.Supplier;
import Java.util.stream.Collectors;
import Java.util.stream.Stream;

public class MyReactiveApp {

    public static void main(String args[]) throws InterruptedException {

        SubmissionPublisher<String> publisher = new SubmissionPublisher<>();

        MySubscriber subs = new MySubscriber();
        publisher.subscribe(subs);


        List<String> strs = getStrs();

        System.out.println("Publishing Items to Subscriber");
        strs.stream().forEach(i -> publisher.submit(i));

        /*while (strs.size() != subs.getCounter()) {
            Thread.sleep(10);
        }*/

        //publisher.close();

        System.out.println("Exiting the app");

    }

    private static List<String> getStrs(){

        return Stream.generate(new Supplier<String>() {
            int i =1;
            @Override
            public String get() {
                return "name "+ (i++);
            }
        }).limit(100).collect(Collectors.toList());
    }

}

サブスクライバー

import Java.util.concurrent.Flow.Subscription;

public class MySubscriber implements Java.util.concurrent.Flow.Subscriber<String>{

    private Subscription subscription;

    private int counter = 0;

    @Override
    public void onSubscribe(Subscription subscription) {
        this.subscription = subscription;
        subscription.request(100);

    }

    @Override
    public void onNext(String item) {
        System.out.println(this.getClass().getSimpleName()+" item "+item);
        //subscription.request(1);
        counter++;

    }

    @Override
    public void onError(Throwable throwable) {
        System.out.println(this.getClass().getName()+ " an error occured "+throwable);

    }

    @Override
    public void onComplete() {
        System.out.println("activity completed");

    }
    public int getCounter() {
        return counter;
    }

}

出力:

Publishing Items to Subscriber
MySubscriber item name 1
MySubscriber item name 2
MySubscriber item name 3
MySubscriber item name 4
MySubscriber item name 5
Exiting the app
MySubscriber item name 6
MySubscriber item name 7
MySubscriber item name 8
MySubscriber item name 9
MySubscriber item name 10
MySubscriber item name 11
MySubscriber item name 12
8
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();

ForkJoinPool.commonPool()を使用して、サブスクライバーへの非同期配信用に新しいSubmissionPublisherを作成します

参照: https://docs.Oracle.com/javase/9​​/docs/api/Java/util/concurrent/SubmissionPublisher.html#SubmissionPublisher--

だから実際には

    strs.stream().forEach(i -> publisher.submit(i));

すべての送信をキューに入れ、別のスレッドで非同期に配信します。しかし、その後、アプリケーションは終了します。これは、ワーカースレッドの進行状況とは無関係です。これは、ワーカースレッドがすでに配信した要素の数に関係なく、アプリケーションが終了することを意味します。

これは、実行ごとに異なる可能性があります。最悪の場合、最初のアイテムが配信される前にアプリケーションが終了する可能性があります。

スレッド

MyReactiveAppのメインメソッドとMySubscriberのonNextでの配信が異なるスレッドで行われることを確認する場合は、対応するスレッドの名前を出力できます。 MyReactiveAppのメイン:

System.out.println(Thread.currentThread().getName()) 

スレッド名としてmainを出力します。

MySubscriberのonNextメソッドは、たとえばForkJoinPool.commonPool-worker-1のようなものを出力します。

ユーザースレッドとデーモンスレッド

実行中のスレッドがまだあるのに、なぜアプリケーションが終了するのですか?

Javaには2種類のスレッドがあります。

  • ユーザースレッド
  • デーモンスレッド

Javaプログラムは、デーモンスレッドがまだ実行されている場合でも、ユーザースレッドが実行されなくなると終了します。

メインスレッドはユーザースレッドです。 SubmissionPublisherは、ここでForkJoinPool.commonPool()のワーカースレッドを使用します。これらはデーモンスレッドです。

すべてのワーカースレッドは、Thread.isDaemon()をtrueに設定して初期化されます。

https://docs.Oracle.com/javase/9​​/docs/api/Java/util/concurrent/ForkJoinPool.html

3