NetBeans

今さらだけど JavaFX で非同期処理

Java JavaFX NetBeans

このエントリーは、JavaFX Advent Calendar 2017 の 6 日目です。

昨日は @planet-az さんの「簡単なミュージックプレーヤーを作ってみた」でした。

明日はこの記事を書いている現時点ではまだ空いてます。(^_^; きっと誰かが素敵な記事を投稿してくれると楽しみにしています。

今さらですが JavaFX の非同期処理を復習がてら簡単にみていきたいと思います。

では、次のようなプログラムを作ってみます。

1

long result = 1;

for (int i = 0; i < repeatProcessingNumber; i++) {

result += result;

初期値 1 で10回ループするプログラムです。

計算終了後は 1024 と結果を表示します。

これだけでは時間のかかる処理とはならないので

TimeUnit.MILLISECONDS.sleep(500);

とスレッドをスリープさせています。

プログラムの状態、計算中の値の表示やキャンセルなどのメッセージ表示、プログレスバーによるプログラムの進捗状態の可視化などを実装します。

思考停止状態でギナギナっと作ったプログラムは次のようなものです。

Warning! This code will make you headache.

You do not have to read it.

それではこのプログラムを実行してみましょう。

2

「START」 ボタンを押した瞬間プロブラムがフリーズしたようになりました。

そして暫くすると結果が表示されました。

3

これは駄目ですね。(>_<。)

プログラムがビジー状態で応答しなくなってしまっては使い物になりません。

進捗状態がわからないのは致命的です。

そして唐突に計算が終了してプログラムが完了する。

そう言えば遠い昔にこのような状態のプログラムが当たり前のように存在していたような気がする。

今の時代こんなのは絶対に許されません。

何がいけないのか?

それは時間のかかる処理を JavaFX アプリケーションスレッド上で行っているからです。

NetBeans のプロファイラで確認してみます。

4

JavaFX Application Thread タイムラインを見てください。

START ボタンを押した瞬間に時間のかかる処理(スレッドスリープ)が走ってます。(緑色から紫色に変わっているところ)

これでは UI の更新処理などはできませんね。

時間のかかる処理をしている間プログラムの応答は無くなり、ビジー状態のようになってしまいます。

つまり、時間のかかる処理用にスレッドをもう一つ起こせばこの問題は解決するはずです。

この問題を解決するのに最適な方法は javafx.concurrentパッケージを使用することです。

javafx.concurrent パッケージは、Worker インタフェースと、2つの具体的な実装である Task および Service クラスで構成されています。

Worker インタフェースは、バックグラウンドスレッド上の Worker オブジェクトの状態、進捗を監視可能なプロパティで公開しています。

状態は ReadOnlyObjectProperty<Worker.State> stateProperty で確認できます。

次のように Enum Worker.State で定義されています。

  • READY     Workerがまだ実行されておらず、実行の準備ができているか、またはWorkerが再初期化されたことを示します。
  • SCHEDULED   Workerの実行がスケジュールされているが、現在は実行中ではないことを示します。
  • RUNNING        このWorkerが実行中であることを示します。
  • SUCCEEDED   このWorkerが正常に完了しており、valueプロパティから読み取る準備ができている有効な結果があることを示します。
  • CANCELLED     このWorkerがWorker.cancel()メソッドによって取り消されたことを示します。
  • FAILED         通常は予期しない条件が発生したことによって、このWorkerが失敗したことを示します。

これらは JavaFX アプリケーションスレッドから使用できます。

Worker オブジェクトによって実行される処理の進捗は、totalWork、workDone、progress など、3つの異なるプロパティを通じて取得できます。

Worker オブジェクトの状態が変化するときに発生するイベントは WorkerStateEventクラスによって指定されます。

これは Task クラスと Service クラスの両方に EventTarget インタフェースが実装され状態イベントのリスニングがサポートされているからです。

今回は再利用の必要が無い Worker オブジェクトを生成するので Task クラスを利用します。

上記のように JavaFX の Task クラスは FutureTask クラスの完全に監視可能にした実装となっています。

この Task クラスを拡張しバックグラウンドスレッドで実行するロジックを実装します。

懐かしのSwingWorker クラスを思い出させてくれますね。(もう、ほとんど覚えてないけどね)

SwingWorker では doInBackground() メソッドにてバックグラウンドスレッドロジックを呼び出します。

JavaFX の場合は call() メソッドです。

doInBackground() メソッド、call() メソッドともにどちらも抽象メソッドなのでオーバーライドして使います。

call() メソッドは Task クラスが実行されるときに呼び出され、call() メソッドに実装されたバックグラウンドスレッドロジックを実行します。

つまり、仕組み的には SwingWorker とほとんど変わりはないので馴染みやすいかも知れません。

さて、JavaFX のために用意されたのにこれだけではあまり意味がありません。

この Task クラスの実行にあたり updateProgress() メソッド、updateMessage() メソッド、updateValue() メソッド、updateTitle() メソッドを呼び出すことができます。

これらを使用するとプログラムの状態、進捗状況、実行中の処理の結果の一部を返すなどを可視化できます。

ちょっと updateValue() メソッドの実装をみてみましょう。

呼び出しが JavaFX アプリケーションスレッドかそうでないかで分岐処理をおこなっています。

JavaFX アプリケーションスレッドからだったらそのまま値を更新します。

でなければ runLater()メソッドで javafx.application.Platform クラスの runLater(Runnable runnable) メソッドを呼び出して値を更新します。

何故なら、JavaFX Sceneグラフは、スレッドセーフではなく、JavaFXアプリケーションスレッドのみアクセスおよび変更できます。

これは Swing では javax.swing.SwingUtilities クラスの invokeLater(Runnable doRun) メソッドと同じようなものです。

JDK1.3 からは java.awt.EventQueue.invokeLater() を呼び出すように作られています。

このinvokeLater(Runnable doRun) メソッドはどのスレッドからも呼び出しが可能となっています。

javafx.application.Platform クラスの runLater(Runnable runnable) メソッドも任意のスレッドから呼び出し可能となっています。

updateProgress() メソッド、updateMessage() メソッド、updateValue() メソッド、updateTitle() メソッドの API ドキュメントには「このメソッドは、任意のスレッドから安全に呼び出すことができます。」と記載されています。

ちなみに updateValue() メソッドは JavaFX 8 からなので上記のようにラムダ式を使い綺麗に書かれています。

古くからある updateMessage() メソッドは次のように Java SE 7 の時代のコードのままとなっています。

このようなのを目にすると Java は開発者のために進化し続けている言語なんだなぁって感慨深いものがありますね。

SwingWorker のように publish() メソッド、process() メソッドなんてのは JavaFX には必要なくなってます。

このようにありがたい機能を標準で実装されている Task クラスですがさらに便利なプロパティを持っています。

これから先程のプログラムを改善するのにいくつかの Task クラスのプロパティを使用します。

それらプロパティは JavaFX のバインドという機能により快適に使用することが可能です。

JSR 295 Beans Binding と同じようなものです。

実際にどのように利用するかというと Task クラスの実行状態(Worker オブジェクト)のプロパティを利用して「START」ボタン、「CANCEL」ボタンの活性化、非活性化をおこないます。

また、バックグラウンドスレッド(Worker オブジェクト)の状態、計算結果の一部を返す処理、プログレスバーによるプログラムの進捗状態の表示もおこないます。

これらの対応を施したプログラムは次のようになります。

このプログラムの実行結果を見てみましょう。

5

ついでに NetBeans のプロファイラでバックグラウンドスレッドが生成されているのを確認しましょう。

6

pool-2-thread-1 がバックグラウンドスレッドとして起動されています。

これで JavaFX アプリケーションスレッドはイベント処理が可能となります。

画面が固まることなくバックグラウンドスレッドの状態、計算結果の一部(途中経過)、プログレスバーも表示されています。

また、「START」ボタン、「CANCEL」ボタンの活性化、非活性化もちゃんとできています。

まず、Task クラスをみていきましょう。

今回はcall() メソッドを実行するために java.util.concurrent の ExecutorService インタフェースを利用しました。

184行目です。

executorService.submit(task);

タスクをパラメータとして指定したスレッドの開始としては次の方法もあります。

Thread th = new Thread(task);
th.setDaemon(true);
th.start();

call() メソッドに時間のかかる処理を記述します。バックグラウンドスレッドロジックです。

call() メソッドが呼ばれたら初期値をセットします。

updateMessage() メソッド、updateValue() メソッド、updateProgress() メソッドはプロパティバインドを利用します。

バックグラウンドスレッドが開始されたらキャンセル操作がリクエストされたかも監視しなければいけません。

キャンセルは java.​util.​concurrent.​FutureTask クラスの isCancelled() メソッドを使います。

今回、スレッドをスリープさせているので InterruptedException をスローする場合があります。

また InterruptedException は Task のキャンセルの結果として発生する場合があるため、確実に InterruptedException を処理し、キャンセル状態を調べる必要があります。

それに対応するために 102 行目の catch 節のところにも isCancelled() メソッドを使用しています。

このようにバックグラウンドスレッドロジック内にブロッキング・コールがある場合は注意が必要となります。

バックグラウンドスレッドでの処理の結果の一部を返すために 110 行目に updateValue() メソッドを使います。

同様に 109 行目で messageLabel 、111 行目で progressBar の更新も行っています。

これらの更新は JavaFX のバインドという便利な機能を使います。

Task クラスのプロパティと Label のテキストプロパティ、ProgressBar の プログレスプロパティとバインドします。

168 行目から次のようにバインドしています。

// textProperty と Workerオブジェクトの stateProperty をバインド
statusLabel.textProperty().bind(task.stateProperty().asString());
// textProperty と Workerオブジェクトの messageProperty をバインド
messageLabel.textProperty().bind(task.messageProperty());
// textProperty と Workerオブジェクトの valueProperty をバインド
interimResult.textProperty().bind(task.valueProperty().asString());
// progressProperty と Workerオブジェクトの progressProperty をバインド
progressBar.progressProperty().bind(task.progressProperty());

これで自動的に Task クラス(Workerオブジェクト)のプロパティ updateXXX() メソッドで更新されたら Label 、ProgressBar も更新されます。

もし、キャンセル操作が実行され、プログラムの実行結果の一部が表示されたままになるのが嫌なら

141 行目の Worker オブジェクトの状態が変化したときのコールバックメソッドである cancelled() メソッドの処理でいったん unbind() メソッドによりバインドを解除して初期値を設定し直すといいでしょう。

@Override
protected void cancelled() {
    super.cancelled();
    updateMessage(“Cancelled!”);
    System.out.println(“Call cancelled()”);
    progressBar.progressProperty().unbind();
    progressBar.setProgress(0.0);
    interimResult.textProperty().unbind();
    interimResult.setText(“INTERIM RESULT”);
}

このようなコールバックメソッドはこの他にもあり、全部で5種類あります。

必要に応じて便利に使えます。

118 行目から 158 行目を参照ください。

バックグラウンドタスクが無事に終了したら 134 行目の succeeded() メソッドがコールバックされます。

@Override
protected void succeeded() {
    super.succeeded();
    updateMessage(“Succeeded!”);
    System.out.println(“Call succeeded()”);
    resultLabel.setText(“RESULT: ” + getValue().toString());
}

ここで Task クラスに戻り値がある場合それを取得します。

戻り値の型はジェネリクスの型パラーメターで指定されたものです。

今回のプログラムでは Long となっています。

必然的に Call() メソッドの戻り値も Long です。

計算結果は 138 行目で javafx.​concurrent.​Task クラスの getValue() メソッドで取得しています。

                resultLabel.setText(“RESULT: ” + getValue().toString());

そして、計算結果を Label のテキストとしてセットしています。

これらのコールバックメソッドは JavaFX アプリケションスレッド上で実行されるので RuntimeException も出ません。

コールバックメソッドを利用する以外にも WorkerStateEvent を使ってイベント処理として扱うこともできます。

160 行目から 165 行目のようにイベントハンドラを登録して使用します。

// WorkerStateEven を使ってWorkerオブジェクトの状態を監視
task.setOnScheduled(wse -> System.out.println(“setOnScheduled”));
task.setOnRunning(wse -> System.out.println(“setOnRunning”));
task.setOnSucceeded(wse -> System.out.println(“setOnSucceeded”));
task.setOnCancelled(wse -> System.out.println(“setOnCancelled”));
task.setOnFailed(wse -> System.out.println(“setOnFailed”));

では、このプログラムを動かしたときの Worker オブジェクトの state プロパティ がどのように変化するか確認します。

プログラムが完する場合次のように出力されます。

setOnScheduled
Call scheduled()
setOnRunning
Call running()
setOnSucceeded
Call succeeded()

この出力結果から

SCHEDULED
RUNNING
SUCCEEDED

と遷移しているのが解ります。

プログラムを途中でキャンセルしてみる場合も確認します。

setOnScheduled
Call scheduled()
setOnRunning
Call running()
setOnCancelled
Call cancelled()
Canceled by InterruptedException.

予想通りの結果ですね。

SCHEDULED
RUNNING
CANCELLED

上記のように遷移しています。これらの動作は想像通りでした。

コールバックメソッドより WorkerStateEvent を使ってイベントハンドラを登録したほうが早い結果となっています。

だからといってメリット、デメリットがあるかどうかは私には解りません。

さて、最後に、「START」ボタン、「CANCEL」ボタンの活性化、非活性化をみてみましょう。

175 行目から 177 行目です。

// disableProperty と Workerオブジェクトの stateProperty をバインド
startButton.disableProperty().bind(task.stateProperty().isEqualTo(RUNNING));
cancelButton.disableProperty().bind(task.stateProperty().isNotEqualTo(RUNNING));

Button の disableProperty と Workerオブジェクトの stateProperty をバインドしているだけです。

startButton は Workerオブジェクトの stateProperty が RUNNING なら非活性化となります。

cancelButton は Workerオブジェクトの stateProperty が RUNNING でなければ非活性化となります。

つまり、Workerオブジェクトの stateProperty が RUNNING の場合だけ活性化します。

これでいちいち操作を行うたびに Button を活性化、非活性化処理を記述しなくてすみます。

このように JavaFX には非同期処理を簡単に扱うことができるようになっています。

SwingWorker を使って非同期処理プログラムを組んでいるのなら JavaFX の javafx.concurrentパッケージを使ってみて時代の流れを感じ取ってみてはいかがでしょうか。

お終い!

I wish you a Merry Christmas.


Java SE 9 の予習 その4

Java NetBeans

Java SE 9 の予習 その4です。

前回のエントリーの続きでおそらく今回も必要とされない無駄なお遊びとなります。

今回は JShell API を利用してアプリケーション内でスニペットの評価や実行を試してみます。

なお、今回のエントリーには 「Java SE 9のProject Kulla、JShellの動作とJShell API (3/4)(JShell API)」の記事を参考にさせていただきました。

ほとんどプログラムは丸写し状態になっていますので元記事をまず読んで理解してこのお遊びプログラムを堪能していただければ幸いです。

下記プログラムは普通に19行目のコンストラクタ内で JShell.create() メソッドによりオブジェクトを生成します。

1

スニペットの評価実行は29行目の eval(String snipetSource) メソッドでおこなわれます。(折り畳んでいるので隠れてます。)

( 最後にいろいろゴニョゴニョ遊んだコードを載せますので行番号はずれてしまいますが参考にしてくださいませ。)

List<SnippetEvent> eval(String snipetSource) メソッドで SnippetEvent オブジェクトを取得します。

JavaDoc では 戻り値:この評価によって直接的または間接的に発生したイベントのリスト となっています。

つまり、ステータスや値です。

種類は、jdk.​jshell.​Snippet クラスの public Kind kind() メソッドで取得します。

戻り値である jdk.jshell.Snippet.Kind は jdk.​jshell.​Snippet のサブクラスを示します。

jdk.jshell.Snippet.Kind は jdk.​jshell.​Snippet クラスの public static enum Kind extends Enum<Kind> となっています。

つまり種類は jdk.​jshell.​Snippet クラスのイミュータブルプロパティです。

jdk.​jshell.​Snippetクラスには 種類を補足するための  public SubKind subKind() メソッドも用意されています。

これは jdk.​jshell.​Snippet クラスの  public static enum SubKind extends Enum<SubKind> を返します。

これはユーザーへのフィードバックに役立ちます。

これは種類の詳細な情報を取得するのに利用されます。

ステータスの取得には jdk.​jshell.​SnippetEvent クラスの public Status status() メソッドを使用します。

戻り値は jdk.​jshell.​Snippet クラスの public static enum Status extends Enum<Status> です。

これは JShell ステート内の動的プロパティです。

したがって JShell クエリで取得されます。

ステータスには次の7つの状態があります。

DROPPED:スニペットは、JShell.drop(Snippet)への明示的な呼び出しのために非アクティブです。

NONEXISTENT:スニペットはまだ存在しないため、非アクティブです。

OVERWRITTEN : 新しいスニペットに置き換えられているため、このスニペットは無効です。

RECOVERABLE_DEFINED:スニペットは、(現在のJShell状態のコンテキストで)回復可能な未解決の参照やその他の問題が本体内にある可能性のある宣言スニペットです。

RECOVERABLE_NOT_DEFINED:スニペットは、(現在のJShell状態のコンテキストで)回復可能な未解決の参照やその他の問題がある可能性のある宣言スニペットです。

REJECTED:スニペットは、初期評価時にコンパイルに失敗し、JShell状態のさらなる変更で有効になることができないため、非アクティブです。

VALID:スニペットは有効なスニペット(現在のJShell状態のコンテキスト内)です。

このプログラムではステータスが VALID でなければ jdk.​jshell.​JShell クラスの public Stream<Diag> diagnostics(Snippet snippet) メソッドでスニペットの最新評価の診断を取得します。

そして jdk.​jshell.​Diag クラスの public abstract String getMessage(Locale locale) メソッドで指定されたロケールのローカライズされたメッセージを返します。

実際のメッセージは実装依存です。 ロケールがnullの場合、デフォルトのロケールを使用します。

値の取得には、jdk.​jshell.​SnippetEvent クラスの public String value() メソッドを使います。

戻り値は成功した実行の結果値。

実行されなかった場合、または例外がスローされた場合、値はnullです。

以上、だいたいこんな感じで JShell API を楽しんでいきたいと思います。

動かすスニペットは例の「よく見かけるネタ IntegerCache 問題」です。

これだけだとさみしいので FunctionalInterface を利用した挨拶プログラムも JShell API で動かしてみましょう。

非常にシンプルなスニペットの実行となります。

Import statement、FunctionalInterface、などのステータス、値、種類などの情報を正確に取得できれば OK です。

では、このプログラムの実行結果を見てみましょう。

FunctionalInterface は、ただの Interface として識別されるようです。

IntegerCache 問題 もちゃんと false となっています。

3

これだと普通すぎて面白くないですよね。

日本ではもうすぐ Java SE 9 がリリースされようという時代に End Of Life となった Java SE 7 でプロフラムを組まなければいけない

涙無くしては語れない非常に過酷な環境に置かれているプログラマが多く存在するらしい。

そこでそういう可愛そうなプログラマのためにコンパイラオプションを JDK7 にし、

IntegerCache 問題で遊ぶために JVM オプションに -XX:AutoBoxCacheMax=128 を設定して実行してみます。

JShell オブジェクトの生成を JShell.create() メソッドから JShell.builder() メソッドに変更します。

4

5

コンパイラオプションの設定は

jdk.​jshell.​JShell.​Builder クラスの public Builder compilerOptions(String… options) メソッドと使い設定します。

可変長引数の使い方が独特で思いっきりドツボにはまったのは内緒です。

JVM オプションの設定は

jdk.​jshell.​JShell.​Builder クラスの public Builder remoteVMOptions(String… options) メソッドを使い設定します。

後は入出力の設定です。

これらは連鎖初期化で使用するため Builderインスタンスを返します。

最後に jdk.​jshell.​JShell.​Builder クラスの public JShell build() メソッドで JShell ステートエンジンを構築します。

これはすべてのJShell機能のエントリーポイントです。

これにより、実行のためのリモートプロセスが作成されます。

それではコンパイラオプションと JVM オプションが正しく機能するかプログラムを走らせてみましょう。

6

うわぁぁ!!

正しく期待通りの動作となったが何故か目から涙がこぼれそうに・・・

IntegerCache 問題の実行結果も true となり正しく JVM オプションが設定されたようです。

これだけで天の邪鬼な私のお遊びは終了です。

ここから先は思い込みでサプライズさせてもらったお話しです。

先のプログラムにコンパイラオプションと JVM オプションを設定しない元の状態に戻して下記スニペットの実行コードを追加してみました。

JShell では import 文を必要としないパッケージが何故か存在します。

java.util.Arrays もその一つだから無事にプログラムは実行されるだろうと思っていました。

7

((((;゜Д゜)))))))  なんと! ERROR! EXPRESSION [ シンボルを見つけられません・・・ ]

JShell API を利用する場合はおもてなしは無しなのか!?

8

しかたないから import 文を追記してみた。

9

今度は期待通りの動作で実行された。

10

ついでだから enum も試してみた。

11

ちゃんと enum 認識していた。

12

実は Linux 環境の NetBeans IDE だからちゃんと日本語対応されているのですが

Windows 環境だと日本語は文字化けしてしまいます。

13

セレブデベロッパーご愛用の IntelliJ IDEA (2017.2 EAP)でも Windows環境での JShellAPI で日本語は文字化けしてしまう。

これは Java SE 9 リリースまでに修正されるのだろうか?

14

ちなみに Windows のコマンドプロンプトからの実行は文字化けしません。

15

以上、JShell API を無駄に使って遊んでみたエントリーでした。

お終い!

おっと!JShell API と戯れた記念コードを載せておきますね。

実行結果は次のようになります。

run:
import java.time.LocalTime;
<< Single-Type-Import Declaration. >>
@FunctionalInterface
interface Greeting<T, R> {
R apply(T time, R message);

        default void sayGreeting(String greeting) {
            greeting(greeting);
        }

        private void greeting(String greeting) {
            System.out.println(greeting + ” from JShellAPI.”);
        };
}
<< Created Interface: Greeting >>
LocalTime now = LocalTime.now();
<< A variable declaration with an initializer expression. >>
now ==> 17:28:24.474604
int hour = now.getHour();
<< A variable declaration with an initializer expression. >>
hour ==> 17
Greeting<Integer, String> greeting
                = (time, message) -> 4 <= time && time < 11 ? “Good morning!”
                        : 11 <= time && time < 18 ? “Good afternoon!”
                                : “Good evening!”;
<< A variable declaration with an initializer expression. >>
greeting ==> $Lambda$17/555826066@a67c67e
String message = null;
<< A variable declaration with an initializer expression. >>
message ==> null
greeting.sayGreeting(greeting.apply(hour, message));
Good afternoon! from JShellAPI.
<< Statement: Snippet:StatementKey#7-greeting.sayGreeting(greeting.apply(hour, message)); >>

(○・ω・)ノ———————————————————-)

Integer x = 128;
<< A variable declaration with an initializer expression. >>
x ==> 128
Integer y = 128;
<< A variable declaration with an initializer expression. >>
y ==> 128
void equivalenceTest(Integer x, Integer y) { System.out.println(x == y); }
<< Created Method: equivalenceTest(Integer,Integer) >>
equivalenceTest(x, y);
false
<< Statement: Snippet:StatementKey#11-equivalenceTest(x, y); >>
(○・ω・)ノ———————————————————-)

import java.util.Arrays;
<< Single-Type-Import Declaration. >>
String str;
<< A variable declaration without initializer. >>
str ==> null
str = “Java”;
<< An assignment expression. >>
str ==> “Java”
String[] elems = str.split(“”);
<< A variable declaration with an initializer expression. >>
elems ==> String[4] { “J”, “a”, “v”, “a” }
System.out.println(Arrays.toString(elems));
[J, a, v, a]
<< Statement: Snippet:StatementKey#16-System.out.println(Arrays.toString(elems)); >>
Arrays.stream(elems).forEach(System.out::println);
J
a
v
a
<< Statement: Snippet:StatementKey#17-Arrays.stream(elems).forEach(System.out::println); >>
(○・ω・)ノ———————————————————-)

public enum JavaPlusYou {

    Japanese(“あなたとJAVA,今すぐダウンロード”),
    English(“JAVA+YOU, DOWNLOAD TODAY!”);

    private final String downloadNow;

    private JavaPlusYou(final String downloadNow) {
        this.downloadNow = downloadNow;
    }

    public String getDownloadNow() {
        return downloadNow;
    }

}
<< Created Enum: JavaPlusYou >>
for (JavaPlusYou jpy : JavaPlusYou.values()) {
            System.out.println(jpy + “: ” + jpy.getDownloadNow());
        }
Japanese: あなたとJAVA,今すぐダウンロード
English: JAVA+YOU, DOWNLOAD TODAY!
<< Statement: Snippet:StatementKey#19-for (JavaPlusYou jpy : JavaPlusYou.values()) {
            System.out.println(jpy + “: ” + jpy.getDownloadNow());
        } >>
(○・ω・)ノ———————————————————-)

System.out.println(System.getProperty(“java.vm.version”));
9-ea+169
<< Statement: Snippet:StatementKey#20-System.out.println(System.getProperty(“java.vm.version”)); >>
System.out.println(System.getProperty(“java.class.version”));
53.0
<< Statement: Snippet:StatementKey#21-System.out.println(System.getProperty(“java.class.version”)); >>
(○・ω・)ノ———————————————————-)

ビルド成功(合計時間: 4秒)

Hatena タグ: ,

食事する哲学者の問題(Dining Philosophers Problem) Semaphore で遊ぶ その2

Java NetBeans

食事する哲学者の問題(Dining Philosophers Problem)を J2SE5.0 で追加された java.util.concurrent.Semaphore を使ってプログラムを組んでみます。

Semaphore については前回のエントリーでざっくり説明し、シンプルなプログラムも紹介しています。

Semaphore で遊ぶ

それでは、食事する哲学者の問題(Dining Philosophers Problem)とは何か調べてみます。

WikiPedia によると、食事する哲学者の問題(Dining Philosophers Problem)とは、並列処理に関する問題を一般化した例である。古典的なマルチプロセスの同期(排他制御)問題である。とされています。

もともとは、1965年、エドガー・ダイクストラは5台のコンピュータが5台のテープ装置に競合アクセスするという同期問題を提示した。

間もなく、この問題はアントニー・ホーアによって「食事する哲学者の問題」に変形して語られることとなった。

どうして食事する哲学者にすり替わったかは置いといて・・・

【問題】

5人の哲学者が食事したり、考え事をしたりしている。

かれらの前には、真ん中にスパゲッティの入った大きなボールが置かれた丸い食卓がある。

その食卓には5枚の皿が置かれ、皿と皿の間にフォークが1本ずつ置かれている。

スパゲッティをボールから取って自分の皿によそうには2本のフォークを必要とし、哲学者は一度に1本のフォークしかテーブルから取ることができない。

(左右の手で同時に両側にある2本のフォークを取ることはできない、という意味。まずどちらかの側のフォークを取ってから、逆の側のフォークを取る。)

哲学者同士は決して会話をしない。

このため、5人が左のフォークを手にとって右のフォークが食卓に置かれるのを待つという危険なデッドロック状態が発生する可能性がある。

スパゲッティではないけどこういう状況ですね。

cake1

【解説】

本来、デッドロック問題の解説手段として使われた。このシステムがデッドロックに到るのは「満たされない要求の円環」が存在する場合である。

例えば、哲学者 P1 が哲学者 P2 の手にしているフォークを待っていて、P2 は哲学者 P3 のものを……といったように円環形に要求が連鎖する。

例えば、それぞれの哲学者が次のように振る舞うとする。

1.左のフォークが得られるまで思索して待ち、フォークを取り上げる。

2.右のフォークが得られるまで思索して待ち、フォークを取り上げる。

3.食事する。

4.右のフォークを置く。

5.左のフォークを置く。

6.最初にもどって繰り返す。

この解法は正しくない。

このやりかたではデッドロックが発生し、全哲学者が左のフォークを持ち、右のフォークが持てるようになるのを待つという状況が発生しうる。

その場合、右のフォークは決して得られない (>_<。)

今回はこの方法でデッドロックを起こさないように Semaphore を使ってリソースへのアクセスを制限し対処するプログラムを組んでみることにします。

これって少し考えてみれば食事をしている両隣の哲学者はフォークをとれない状況ができるので同時に食事出来る哲学者の最大数は5人の場合は二人となります。

つまり、使われるフォークの数は4本となります。

これを利用すれば比較的簡単に Semaphore を利用してプログラムを組むことができますね。

それではプログラムを組んでみることにしますが、素直に哲学者に登場していただくより綺麗な女性を食事にお誘いしました。

今回デッドロックを避けるために使う Semaphore はウェイターの役割をはたします。

つまり、スパゲッティを食べる権利( パーミット取得 )があることを許可してくれます。

綺麗な女性は左のフォーク、そして右のフォークと使うことができればスパゲッティを食べることができます。

ただし、隣の女性がフォークを使っていたら( ロックがかかっていたら )フォークがあくのを待たなければ( ロックが解放される )いけません。

ウエイターは綺麗な女性がスパゲッティを食べ終わり左右のフォークを置いたなら、

その綺麗な女性のスパゲッティを食べる権利( パーミット )を戻してもらい、

次に待機している綺麗な女性がいれば「どうぞ、お食べください」とスパゲッティを食べる権利( パーミット )を与えます。。

そして綺麗な女性はフォークが空いていれば手に取りスパゲッティを食べられます。

フォークが空いていなければ待ちます。

これの繰り返しです。

最大二人の綺麗な女性がスパゲッティを同時に食べることがこれで可能となります。

左右のフォークの使用状況( ロックの状態 )によりフォーク待ち時間が発生してしまいますがこれでデッドロックは解消されます。

きっと綺麗な女性達は美味しいスパゲッティをお腹いっぱい食べて満足してお帰りになられたでしょう。

これをプログラムにすると次のようになりました。

プログラムを実行させてデッドロックが発生せず動作するか確認します。

桐谷美玲 左のフォークをとりあげる思索した時間は、0秒7838E-5
北川景子 左のフォークをとりあげる思索した時間は、0秒8168E-5
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒313E-6
桐谷美玲 1回目の食事
桐谷美玲 1回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、1秒3359217400000001
北川景子 1回目の食事
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
北川景子 1回目食事終了
剛力彩芽 右のフォークをとりあげる思索した時間は、1秒3721449000000001
剛力彩芽 1回目の食事
堀北真希 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
堀北真希 1回目の食事
剛力彩芽 1回目食事終了
堀北真希 1回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒375070565
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 2回目の食事
新垣結衣 1回目の食事
新垣結衣 1回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 2回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒08200188600000001
北川景子 2回目の食事
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒652E-6
北川景子 2回目食事終了
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒583391387
堀北真希 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 2回目の食事
剛力彩芽 2回目の食事
堀北真希 2回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒652E-6
剛力彩芽 2回目食事終了
新垣結衣 右のフォークをとりあげる思索した時間は、0秒436658855
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒652E-6
新垣結衣 2回目の食事
桐谷美玲 3回目の食事
新垣結衣 2回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒294E-6
桐谷美玲 3回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒23805942200000002
北川景子 3回目の食事
堀北真希 左のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
堀北真希 3回目の食事
北川景子 3回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒312E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
剛力彩芽 3回目の食事
堀北真希 3回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒652E-6
剛力彩芽 3回目食事終了
新垣結衣 右のフォークをとりあげる思索した時間は、0秒18132323900000002
新垣結衣 3回目の食事
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 4回目の食事
新垣結衣 3回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 4回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒39374600600000004
北川景子 4回目の食事
堀北真希 左のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒312E-6
堀北真希 4回目の食事
堀北真希 4回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒9640000000000005E-6
北川景子 4回目食事終了
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒023616073
剛力彩芽 4回目の食事
新垣結衣 左のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
剛力彩芽 4回目食事終了
新垣結衣 右のフォークをとりあげる思索した時間は、0秒7176410350000001
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒312E-6
新垣結衣 4回目の食事
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 5回目の食事
桐谷美玲 5回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
新垣結衣 4回目食事終了
堀北真希 右のフォークをとりあげる思索した時間は、0秒718831517
堀北真希 5回目の食事
北川景子 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
北川景子 右のフォークをとりあげる思索した時間は、0秒3543E-5
北川景子 5回目の食事
堀北真希 5回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒6430000000000004E-6
北川景子 5回目食事終了
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒140569047
剛力彩芽 5回目の食事
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒981E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 6回目の食事
桐谷美玲 6回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒6420000000000003E-6
剛力彩芽 5回目食事終了
新垣結衣 右のフォークをとりあげる思索した時間は、0秒256017732
新垣結衣 5回目の食事
堀北真希 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
新垣結衣 5回目食事終了
堀北真希 右のフォークをとりあげる思索した時間は、0秒908995176
堀北真希 6回目の食事
北川景子 左のフォークをとりあげる思索した時間は、0秒973E-6
北川景子 右のフォークをとりあげる思索した時間は、0秒313E-6
北川景子 6回目の食事
堀北真希 6回目食事終了
北川景子 6回目食事終了
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒156232277
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 7回目の食事
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
剛力彩芽 6回目の食事
桐谷美玲 7回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒312E-6
剛力彩芽 6回目食事終了
新垣結衣 右のフォークをとりあげる思索した時間は、0秒200991297
新垣結衣 6回目の食事
堀北真希 左のフォークをとりあげる思索した時間は、0秒652E-6
新垣結衣 6回目食事終了
堀北真希 右のフォークをとりあげる思索した時間は、1秒3822943870000002
堀北真希 7回目の食事
北川景子 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
北川景子 右のフォークをとりあげる思索した時間は、0秒3030000000000004E-6
北川景子 7回目の食事
堀北真希 7回目食事終了
北川景子 7回目食事終了
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒089658099
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 8回目の食事
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒625000000000001E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒313E-6
剛力彩芽 7回目の食事
剛力彩芽 7回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒955E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒652E-6
新垣結衣 7回目の食事
桐谷美玲 8回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
新垣結衣 7回目食事終了
堀北真希 右のフォークをとりあげる思索した時間は、0秒8264462760000001
北川景子 左のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
堀北真希 8回目の食事
北川景子 右のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
北川景子 8回目の食事
堀北真希 8回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒6420000000000003E-6
北川景子 8回目食事終了
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒217371327
剛力彩芽 8回目の食事
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒312E-6
桐谷美玲 9回目の食事
剛力彩芽 8回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
新垣結衣 8回目の食事
桐谷美玲 9回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
新垣結衣 8回目食事終了
堀北真希 右のフォークをとりあげる思索した時間は、1秒21894861600000004
北川景子 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
堀北真希 9回目の食事
北川景子 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
北川景子 9回目の食事
堀北真希 9回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒652E-6
北川景子 9回目食事終了
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒780406594
剛力彩芽 9回目の食事
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒294E-6
桐谷美玲 10回目の食事
剛力彩芽 9回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒973E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒652E-6
新垣結衣 9回目の食事
桐谷美玲 10回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
新垣結衣 9回目食事終了
堀北真希 右のフォークをとりあげる思索した時間は、0秒475458529
堀北真希 10回目の食事
北川景子 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
北川景子 右のフォークをとりあげる思索した時間は、0秒312E-6
北川景子 10回目の食事
堀北真希 10回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒652E-6
北川景子 10回目食事終了
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒34860347900000005
新垣結衣 左のフォークをとりあげる思索した時間は、0秒652E-6
剛力彩芽 10回目の食事
剛力彩芽 10回目食事終了
新垣結衣 右のフォークをとりあげる思索した時間は、1秒0036094330000000507
新垣結衣 10回目の食事
新垣結衣 10回目食事終了

堀北真希 が 10回食事するのに要した時間は、28秒6504769030000013でした。
新垣結衣 が 10回食事するのに要した時間は、30秒9585105100000035でした。
剛力彩芽 が 10回食事するのに要した時間は、30秒0028747960000004014でした。
北川景子 が 10回食事するのに要した時間は、28秒998852129000003でした。
桐谷美玲 が 10回食事するのに要した時間は、27秒3586783140000023でした。

(○・ω・)ノ——– I eat delicious spaghetti and am stuffed. ——–(^_^)

ちゃんと動きました。(^_^)

このプログラムでは Semaphore インスタンス生成時のコンストラクタ引数に哲学者の人数を割り当てました。

また、公平性も設定しました。

パーミット数の設定は同時アクセスは 2 スレッドまでなので 2 と設定して、acquire()、release() とパーミット取得数とパーミット解放数を 1 してもいいでしょう。

今回は、パーミット数の設定を 5 , acquire(2)、release(2) とパーミット取得数とパーミット解放数を 2 としました。

同時アクセス可能なのが 2 スレッドで左右のフォークをロックするのでこの問題の鍵となるフォークの数を考慮にいれました。(あまり意味の無いこだわり)

final Semaphore semaphore = new Semaphore(PHILOSOPHERS_NUMBER, true);

NetBeans のプロファイラでスレッドの状態を確認してみましょう。

1

5 個の ForkJoinPool.commonPool-worker-29 とかのスレッドが最大で二つ同時アクセスされているのが確認できます。

スレッド終了後にフォークのロック解放待ちが無い場合はすぐに新たなスレッドが実行されているのも確認できます。

Semaphore って本当に便利ですね!

さて、ここでちょっとしたことを試してみよう。

Semaphore の公平性の設定をしなかったらどうなるのか?

final Semaphore semaphore = new Semaphore(PHILOSOPHERS_NUMBER); // 公平性無し

では、プログラムを実行させてみます。

剛力彩芽 左のフォークをとりあげる思索した時間は、0秒0810000000000002E-5
堀北真希 左のフォークをとりあげる思索した時間は、0秒0810000000000002E-5
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
剛力彩芽 1回目の食事
堀北真希 1回目の食事
堀北真希 1回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒313E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 2回目の食事
剛力彩芽 1回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒312E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒652E-6
剛力彩芽 2回目の食事
堀北真希 2回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒312E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 3回目の食事
剛力彩芽 2回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒910000000000001E-7
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
剛力彩芽 3回目の食事
堀北真希 3回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
堀北真希 4回目の食事
剛力彩芽 3回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒910000000000001E-7
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
剛力彩芽 4回目の食事
堀北真希 4回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 5回目の食事
剛力彩芽 4回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒6420000000000003E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
剛力彩芽 5回目の食事
堀北真希 5回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒312E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
堀北真希 6回目の食事
剛力彩芽 5回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒910000000000001E-7
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒652E-6
剛力彩芽 6回目の食事
剛力彩芽 6回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒312E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
剛力彩芽 7回目の食事
堀北真希 6回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒910000000000001E-7
堀北真希 右のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 7回目の食事
剛力彩芽 7回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
剛力彩芽 8回目の食事
堀北真希 7回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒910000000000001E-7
堀北真希 右のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 8回目の食事
剛力彩芽 8回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒973E-6
剛力彩芽 9回目の食事
堀北真希 8回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒6430000000000004E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
堀北真希 9回目の食事
剛力彩芽 9回目食事終了
剛力彩芽 左のフォークをとりあげる思索した時間は、0秒652E-6
剛力彩芽 右のフォークをとりあげる思索した時間は、0秒652E-6
剛力彩芽 10回目の食事
堀北真希 9回目食事終了
堀北真希 左のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
堀北真希 右のフォークをとりあげる思索した時間は、0秒652E-6
堀北真希 10回目の食事
剛力彩芽 10回目食事終了
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒312E-6
堀北真希 10回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒616E-6
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒830071894
桐谷美玲 1回目の食事
桐谷美玲 1回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒888391644
北川景子 1回目の食事
北川景子 1回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒6420000000000003E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、1秒44702734600000005
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 2回目の食事
桐谷美玲 2回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、1秒2932113780000001
北川景子 2回目の食事
北川景子 2回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒3030000000000004E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒6195947270000001
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒312E-6
桐谷美玲 3回目の食事
桐谷美玲 3回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、1秒23741232
北川景子 3回目の食事
北川景子 3回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒6420000000000003E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、1秒2788060170000002
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 4回目の食事
桐谷美玲 4回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒9319671190000001
北川景子 4回目の食事
北川景子 4回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒633E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒55758825
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒3543E-5
桐谷美玲 5回目の食事
桐谷美玲 5回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒7469512070000001
北川景子 5回目の食事
北川景子 5回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒910000000000001E-7
桐谷美玲 左のフォークをとりあげる思索した時間は、1秒08432603500000013
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒973E-6
桐谷美玲 6回目の食事
桐谷美玲 6回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒733817256
北川景子 6回目の食事
北川景子 6回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、1秒1698336330000001
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒312E-6
桐谷美玲 7回目の食事
桐谷美玲 7回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、1秒4533695360000001
北川景子 7回目の食事
北川景子 7回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒322E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒8525430700000001
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 8回目の食事
桐谷美玲 8回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、0秒669598278
北川景子 8回目の食事
北川景子 8回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、1秒3454052980000002
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
桐谷美玲 9回目の食事
桐谷美玲 9回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、1秒25630280000000005
北川景子 9回目の食事
北川景子 9回目食事終了
北川景子 左のフォークをとりあげる思索した時間は、0秒652E-6
桐谷美玲 左のフォークをとりあげる思索した時間は、0秒598060677
桐谷美玲 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
桐谷美玲 10回目の食事
桐谷美玲 10回目食事終了
北川景子 右のフォークをとりあげる思索した時間は、1秒09184714699999996
北川景子 10回目の食事
新垣結衣 左のフォークをとりあげる思索した時間は、0秒946E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒312E-6
新垣結衣 1回目の食事
北川景子 10回目食事終了
新垣結衣 1回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒973E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
新垣結衣 2回目の食事
新垣結衣 2回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒6430000000000004E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
新垣結衣 3回目の食事
新垣結衣 3回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒312E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒652E-6
新垣結衣 4回目の食事
新垣結衣 4回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒322E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
新垣結衣 5回目の食事
新垣結衣 5回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒024E-5
新垣結衣 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
新垣結衣 6回目の食事
新垣結衣 6回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒973E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
新垣結衣 7回目の食事
新垣結衣 7回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒6430000000000004E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒9820000000000003E-6
新垣結衣 8回目の食事
新垣結衣 8回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒652E-6
新垣結衣 右のフォークをとりあげる思索した時間は、0秒3210000000000001E-6
新垣結衣 9回目の食事
新垣結衣 9回目食事終了
新垣結衣 左のフォークをとりあげる思索した時間は、0秒910000000000001E-7
新垣結衣 右のフォークをとりあげる思索した時間は、0秒6510000000000002E-6
新垣結衣 10回目の食事
新垣結衣 10回目食事終了

堀北真希 が 10回食事するのに要した時間は、10秒9469584730000005でした。
新垣結衣 が 10回食事するのに要した時間は、40秒20581136300000225でした。
剛力彩芽 が 10回食事するのに要した時間は、10秒11629695400000095でした。
北川景子 が 10回食事するのに要した時間は、31秒21609595200000342でした。
桐谷美玲 が 10回食事するのに要した時間は、30秒20136357100000168でした。

(○・ω・)ノ——– I eat delicious spaghetti and am stuffed. ——–(^_^)

うわぁぁぁ・・・ 思いっきり不公平じゃないですか!

NetBeans のプロファイラでスレッドの状態を確認してみます。

2

今回のようなフォークのロック解放待ちがあるような場合はまずいような・・・

こんなとこもあるよね。

3

プログラムの処理時間が大幅に増えてます。

公平性の設定はよく考えないと思わぬ結果をもたらすかもしれませんね。

CompletableFuture を使えば楽に並行処理を記述できるし、Java って世に出たときはマルチスレッド対応だぜ!って誇らしげに売りにしていた。

あれから20年、確実に進化しています。

どんどん進化していく並行処理を日本語で優しく解説しているサイトはあまり無いのが残念です。

便利なものほど正しく使わないと痛い目をみますからね。

もっと正しい知識を優しい日本語の解説で入手したい今日この頃です。

今月の私は健康診断でコレステロールと中性脂肪の値が高いと指摘されたにもかかわらずこういった美味しいケーキをたくさん食べてしまいました。

c4

c3

c2

c5

美味しさそのままで中性脂肪とコレステロールの値をさげるケーキって無いものか。

お終い!

Hatena タグ:

Semaphore で遊ぶ

Java NetBeans

早いもので今日は五月五日 こどもの日です。

祝日法2条によれば、「こどもの人格を重んじ、こどもの幸福をはかるとともに、母に感謝する」となっています。

5月5日は古来から端午の節句として日本では男子の健やかな成長を祈願し各種の行事を行う風習があります。

ということで、私も男子の健やかな成長を祈願するプログラムを組むことにしました。

今さらですが、J2SE5.0 で追加された java.util.concurrent.Semaphore をつかってふざけた 夢のようなプログラム組みました。

男子の健やかな成長には素敵な女性との出会いは必要不可欠です。

だから4人の素敵な女性にデートに誘われると言う内容です。

しかし、体は一つしかありませんので一度に4人では収集付かなくなります。

そこで恭平さんにヘルプをお願いしました。(^_^;)

これでも、素敵な女性4人に対して男子は二人です。

これでなんとかするために Semaphore を使って二組のカップルまでがデートに出かけられるとします。

残りの二人の女性は先に出かけた二組のカップルのどちらか片方が帰ってきたらデートに出かけるという設定です。

つまり、男子二人というリソースに同時アクセス出来るのは素敵な女性スレッド二つまでということになります。

それが Semaphore を使えば実現できてしまうのです。

Semaphore はざっくり説明するとリソースに対するアクセスが可能か不可能かのいずれかの値をとる Binary Semaphore と

アクセス可能なリソース数を任意に設定することが出来る Counting Semaphore があります。

Java で採用された Semaphore は Counting Semaphore にあたります。

有限リソースに対して並行アクセス可能なスレッド数を自由に設定できます。

今回組むプログラムにうってつけの優れものとなっています。(^_^)

17 行目で Semaphore を生成しています。

コンストラクタの引数はパーミット数と公平性を設定します。

今回のプログラムではパーミット数はリソース数と同じ 2 となっています。

公平性は API ドキュメントを見ると今回のプログラムの場合は優しい私はパフォーマンスより平等性を重視して true と設定しました。

ちなみに API ドキュメントによると

このクラスのコンストラクタは、オプションで公平性パラメータを受け入れます。
falseに設定すると、このクラスはスレッドがパーミットを取得する順序について保証しません。
特に、バージ(barging)が許可されています。
つまり、acquire()を呼び出すスレッドに、待機していたスレッドより先にパーミットを割り当てることができます。
論理的には、新しいスレッドが、待機中のスレッドのキューの先頭に配置されます。
公平性がtrueに設定されると、セマフォは、acquireメソッドのいずれかを呼び出すスレッドが、これらのメソッドの呼出しが処理された順序(先入れ先出し、FIFO)でパーミットを取得するように選択されることを保証します。
FIFO順序付けは、必然的にこれらのメソッド内の特定の内部実行ポイントに適用されます。
そのため、あるスレッドが別のスレッドより前にacquireを呼び出しても、そのスレッドよりあとに順序付けポイントに到達する可能性があります。
また、メソッドからの復帰時も同様です。また、時間指定のないtryAcquireメソッドは公平性の設定に従いませんが、利用可能なパーミットをすべて取得することにも注意してください。

となっています。

実は今回のプログラムではこの公平性の設定の違いというのは解りにくいのですが

食事する哲学者の問題Dining Philosophers Problem)を Semaphore を使った簡易的なプログラムを組んでみるとよく解ります。

次に Semaphore からパーミットを取得するために acquire() メソッドを使います。

このプログラムでは 23 行目で使っています。

public void acquire() throws InterruptedException は API ドキュメントによると次のようになっています。

このセマフォからパーミットを取得します。パーミットが利用可能になるか、またはスレッドが割り込みされるまでブロックします。
パーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。

パーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の2つのいずれかが起きるまで待機します。
•ほかのスレッドがこのセマフォに対してrelease()メソッドを呼び出し、現在のスレッドが次にパーミットを割り当てられるスレッドになる。
•ほかのスレッドが現在のスレッドに割り込みを行う。

現在のスレッドで、
•このメソッドへのエントリ上で設定された割込みステータスが保持されるか、
•パーミットの待機中に割り込みが発生した場合、
InterruptedExceptionがスローされ、現在のスレッドの割込みステータスがクリアされます。例外:InterruptedException – 現在のスレッドで割込みが発生した場合

これとは別に引数で取得するパーミット数を設定できるメソッドもあります。

次は取得したパーミットを解放する release() メソッドを調べてみます。

33 行目で使っています。

デートが終了したらパーミットを保持する必要はないので解放します。

public void release() は API ドキュメントによると次のように書かれています。

パーミットを解放し、セマフォに戻します。
パーミットを解放すると、利用可能なパーミットの数が1つずつ増えます。
いくつかのスレッドがパーミットを取得しようと試みている場合は、その中の1つのスレッドが選択され、解放されたばかりのパーミットが与えられます。
そのスレッドは、スレッドのスケジューリングに関して(ふたたび)有効になります。

パーミットを解放するスレッドは、acquire()の呼出しでそのパーミットを取得している必要はありません。
セマフォの適切な使用法は、アプリケーションでのプログラミング規約で確立されます。

たったこれだけでリソースにアクセスできる並行スレッド数を制限できてしまうんですねぇ・・・

このプログラムの実行結果は次のようになります。

待っているのは、堀北真希
待っているのは、北川景子
待っているのは、新垣結衣
待っているのは、剛力彩芽
今からデートに行くのは、北川景子
今からデートに行くのは、堀北真希
北川景子 は ゆっち とデートに行きました。
堀北真希 は 恭平 とデートに行きました。
北川景子 は ゆっち とデートから帰ってきました。
今からデートに行くのは、新垣結衣
新垣結衣 は ゆっち とデートに行きました。
新垣結衣 は ゆっち とデートから帰ってきました。
今からデートに行くのは、剛力彩芽
剛力彩芽 は ゆっち とデートに行きました。
剛力彩芽 は ゆっち とデートから帰ってきました。
堀北真希 は 恭平 とデートから帰ってきました。
(○・ω・)ノ——– Are you happy? ——–

さて、疑い深い私は NetBeans のプロファイラでスレッドの状態を確認してみました。( 注意:先ほどの実行結果とはことなります。)

1a

期待通りの動きをしてくれてます。(^_^) NetBeans のプロファイア手軽に使えて便利だね。

Semaphore 使える良い娘じゃないか!

もし、あなたが 恭平さん に素敵な女性とデートさせずに全ての女性を独り占めしたいとしたらこのプログラムをどのように変更したらいいでしょうか?

答えは簡単ですね!

私はヘルプを頼んだ 恭平さん に悪いのでプログラムの変更はいたしません。

どうしてもというかたはご自由に。(^_^;)

ちなみに今回のプログラムでは

CompletableFuture<Void> lovers1 = CompletableFuture.runAsync(() -> maki.play(), executor);

として ExecutorService を使いましたが

CompletableFuture<Void> lovers1 = CompletableFuture.runAsync(() -> maki.play());

として Fork/Join Framework を使うのもありです。

こどもの日の休日スペシャルはこれでお終いです。

食事する哲学者の問題Dining Philosophers Problem)に続く かも?

Hatena タグ: ,

NetBeans IDE から SSH 接続で GitHub を使ってみた。

Java NetBeans Ubuntu

お盆休みも今日が最後になったのにダラダラして終わることになった。(^_^;

ビール飲んでグダグダしながらこんなことを今さらしていたので記念にエントリーしておく。

最初に GitHub に新しいリポジトリを作成する。

そしてまだ SSD 公開鍵を設定してなければ設定しておく。

1

 

NetBeans IDE で新規プロジェクトを作成する。

2

 

Git ローカルリポジトリを作成する。

3

 

ローカルリポジトリを作成する場所を指定する。

4

 

出力ウィンドウでローカルリポジトリが作成されたことが解る。

5

 

プロジェクトウィンドウでもリポジトリのバッジが付いたことが確認できる。

6

 

HelloGitHub.java ファイルを add する。

7

 

そしてコミットする。

8

 

コミットされるファイルを確認し、解りやすいメッセージを書いておく。

GitHub は公開だと全世界と繋がるので地球標準の英語でメッセージを書いておくと良いことがあるかもしれない。

9

 

ローカルリポジトリにコミットした状態を Git リポジトリ・ブラウザで確認。

まだ寂しい状態であることが一目で解りますね。(ちょっと表現がおかしいけど気にしない。プレミアム・モルツのせいだ!)

10

 

それではリモートリポジトリにプッシュしましょう。

11

 

はじめてリモートリポジトリに接続する場合は下図のように必要事項を設定します。

これは SSH 接続を利用しています。(OpenSSH)

一度設定してしまえば後はラクチン、ラクチンです。

12

 

接続が完了すると下図のような画面が表示されます。

16

 

チェックをいれて次へ進めます。

17

 

終了ボタンを押してプッシュを実行します。

18

 

リモートリポジトリへのプッシュが完了したことを Git リポジトリ・ブラウザで確認します。

19

 

疑り深い人は GitHub のサイトをブラウザで開いて確認してみてください。

20

 

さらにソースコードを編集します。

21

 

変更されたことが色づけとバージョンラベルで確認できます。

22

 

さっきと同様にローカルリポジトリをコミットします。

23

 

そしてリモートリポジトリへプッシュします。

今度は先ほど設定したリモートリポジトリが選択されているので「次へ」ボタンを押すだけです。

24

 

25

 

26

 

さて、疑り深い人は GitHub のサイトをブラウザで開いて確認ですね。

27

 

完璧ですね。

28

 

ついでに履歴も確認。

29

 

30

 

NetBeans IDE で Git をそれなりに使うには十分ですね。

ただ、大規模なプロジェクトだと専用のツールを使ったほうが痒いところに手が届きます。

必要十分な機能は備えているみたいなんでやっぱり NetBeans IDE は素晴らしい!

Hatena タグ: ,,

« 古い記事