質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

3回答

1484閲覧

フラグを監視するプログラム

hi_se_pr

総合スコア21

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

4クリップ

投稿2018/05/05 02:29

編集2018/05/05 03:23

タイトルの通り、あるクラスのフラグを監視するプログラムを書いています(あるプログラムの一部として)。

メインスレッドとは別に、1つスレッドを走らせてその中でwhile文を使ってメイン関数を持つクラスのフラグを監視しています。

Java

1@Override 2public void run(){ 3 while(true){ 4 if(Test.flag){ 5 //はじめflagはfalse 6 break; 7 } 8 } 9 System.out.println("while文を抜けました"); 10}

単純にこのようにかくとflagをメイン関数内でtrueに変えてもwhile文を抜け出すことができないのですが、while文の中にThread.sleep()関数を使って少し待つとwhile文を抜け出すことができました。

こういったflgaなどを監視するプログラムを作る場合Thread.sleepやTimerを使って待たなければwhile文を抜け出すことはできないのでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/05/05 02:55

抜けてないのに「while文を抜けました」出力するのは置いておいて スリープしてないとこの無限ループにCPUリソースがとられ過ぎますが数倍の時間をかけて終わるとは思いますが。
退会済みユーザー

退会済みユーザー

2018/05/05 02:56

あとは対象が static であるならばですね
hi_se_pr

2018/05/05 03:24

このような監視プログラムを書く場合どういうコードを書くのが適切なのでしょうか
guest

回答3

0

ベストアンサー

訂正:umyuさんご指摘の点を訂正します。

Thread.sleepやTimerを使って待たなければwhile文を抜け出すことはできないのでしょうか?

いえ、必ずしもそうではありません。質問者さんのコードの詳細が不明なので抜け出せなかった原因をはっきりと指摘できませんが、~~少なくともTest.flagはvolatile修飾子を付与しなければなりません。~~例えば、Test.flagvolatile修飾子を付与するといった「実行順序の同期に関する配慮」をしなければなりません。理由については「メモリバリア」「メモリフェンス」といった用語を調べるとよいと思います。

java

1class Test { 2 static volatile boolean flag; 3 ... 4}

ただ通常はご質問のような方法(CPUを消費し続けながら期待する状態変化を検出すること=ビジーループ)は使いません。そうではなく「変化がおきるまでスレッドをブロックしてくれるような機構」を用います。Javaでそれを行う最も原始的な方法はsynchronized文Object#waitObject#notifyになります。

java

1class Test { 2 Object lock = new Object(); // 参照型のインスタンスなら何でもよい 3 boolean state; //(注) 4 5 // 状態を変更する側(スレッド#1で動かす) 6 void change() throws InterruptedException { 7 synchronized (lock) { 8 state = true; 9 lock.notify(); 10 } 11 } 12 13 // 状態変化を待つ側(スレッド#2で動かす) 14 void waitForChange() throws InterruptedException { 15 synchronized (lock) { 16 while (!state) { 17 lock.wait(); 18 } 19 } 20 } 21 ... 22}

(注:元の回答ではvolatileを付与していましたが、stateのread/writeが必ずsynchronizedブロック内にしかない上のような例ではvolatileは必要ないので上の例からvolatileを取りました)

lockはどんな参照型インスタンスでもよく、上の例では例えばthisとかTest.classを使ってもかまいません。thisを使うなら

java

1 void synchronzied change() throws InterruptedException { 2 state = true; 3 lock.notify(); 4 }

と書けますしTest.classを使うなら

java

1 static void synchronzied change() throws InterruptedException { 2 state = true; 3 lock.notify(); 4 }

と書けます。一定時間sleepなどで待機しながら変化を待つこともできますが、「状態が変化してもsleepで待っている間は変化を検知できないので即時性に劣る方法」ということになります。

投稿2018/05/05 03:50

編集2018/05/07 08:56
KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hi_se_pr

2018/05/07 05:30

わかりやすい説明ありがとうございます! スレッドの制御でうまく処理できました
umyu

2018/05/07 06:39 編集

>KSwordOfHasteさんへ synchronzied を使う限りでは、volatile宣言 は不要です。 https://www.ibm.com/developerworks/jp/java/library/j-jtp10264/index.html ◇↓はjava.util.concurrent.atomic.AtomicBooleanを使った同期サンプルです。 https://wiki.sei.cmu.edu/confluence/display/java/VNA00-J.+Ensure+visibility+when+accessing+shared+primitive+variables ◇LCK00-J. 信頼できないコードから使用されるクラスを同期するにはprivate finalロックオブジェクトを使用する https://www.jpcert.or.jp/java-rules/lck00-j.html 以上、参考情報としてだけ置いておきます。横レス失礼致しました。
KSwordOfHaste

2018/05/07 06:45

ご指摘ありがとうござました。以前本サイトでvolatileが必要という話を訊いたのですが理解がまだ曖昧だったようです。再度確認してみます。
KSwordOfHaste

2018/05/07 08:58 編集

以前https://teratail.com/questions/79476でyohhoyさんにvolatileが必要と指摘していただいたのですが、そのケースはsynchronized有りと無しの2つのブロックでの同一の変数アクセスというかなり特殊なケースだったためvolatileが必要で、本件の自分の回答にあるように参照・更新が全てsynchronizedブロックに含まれる通常のケースではvolatileは不要であるという点が理解できていませんでした。言語仕様書の17.4 Memory Modelが仕様の全容であると思いますが、完璧に理解するのがさらりとできるほどに自分にとって易しい内容ではなかったです。じっくり理解しようと思います。 とりあえずは本回答を適切に直せるように考えてみます。 --- ==>回答を訂正してみました。05/07 17:58
guest

0

マルチスレッドと言っても、コードが完全に並行して実行されるというもんでもないです。
実行されるコードは常に一つで、それを切り替えて複数実行されるように見せてるだけなんですね。
んで、そのコードを切り替えるタイミングですが、一つはコードが待ちに入ったとき(Thread.Sleepや各種event、システムコール呼び出しなどなど)、と、あとはタイマ割り込みなどの時間的なイベントがあったとき、となります。
その時間的なイベント、ですが、実際にどんだけの時間スロットで切り替えられるというのはよくは知りませんが、そこらへんぐぐると、いろいろ解説が出てくると思いますんで調べてみてください。

ということで、提示されてるコードだと、sleepがない場合、次にタスクの切り替えが起きるのは数ms後になる(かもしれない?)ということになるってことになりますね

投稿2018/05/05 03:48

y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hi_se_pr

2018/05/07 05:32

回答ありがとうございました! OSの授業を取っていたのでなんとなくイメージは湧いていたのですが実際書いたコードがどのように動いてるかもっと詳しく勉強した方が為になりそうですね、、、
guest

0

デザインパターンの「garded wait」と呼ばれるものがあります。↓動作未確認。

java

1@Override 2public void run(){ 3 while(!Test.flag){ 4 //はじめflagはfalse 5 wait();//←追加しました 6 } 7 System.out.println("while文を抜けました"); 8} 9 10//別の場所 11Test.flag = true; 12notifyAll(); 13//↑の二行で当該スレッドがループを脱出する。

投稿2018/05/05 04:01

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hi_se_pr

2018/05/07 05:31

回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問