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

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

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

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

Q&A

解決済

1回答

4586閲覧

LinkedBlockingQueueクラスの利用

Matt

総合スコア41

Java

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

0グッド

0クリップ

投稿2017/03/16 05:16

###分からないこと
現在、教材でThreadの勉強をしています。LinkedBlockingQueueクラスを利用したサンプルコードがあるのですが、実行結果がなぜそうなるのかわかりません。

3つofferで要素を追加 → 3つpollで要素を削除
これの繰り返しにならないのでしょうか?

また、実行結果の4,5行目でpollで要素を2つ削除しているのに6行目でofferのあと、要素数が3になっているのも謎です。

###ソースコード
import java.util.concurrent.*;

public class Sample10_9 {
public static void main(String[] args) {
BlockingQueue<Double> queue = new LinkedBlockingQueue<>(3);
new Thread(() -> { //キューに要素を追加するスレッド
while(true) {
try {
queue.offer(Math.random(), 2, TimeUnit.SECONDS);
System.out.println("offer() : " + queue.size());
} catch (InterruptedException e) { e.printStackTrace(); }
}
}).start();

new Thread(() -> { //キューから要素を取得および削除するスレッド while(true) { try { double pNum = queue.poll(2, TimeUnit.SECONDS); System.out.println("poll() : " + pNum); } catch (InterruptedException e) { e.printStackTrace(); } } }).start();

}
}

###実行結果
offer(): 1
offer(): 2
offer(): 3
poll(): 0.24829714919143642
poll(): 0.9951008257327133
offer(): 3
poll(): 0.2502551360660312
poll(): 0.931467810599396
poll(): 0.08339082618949567
offer(): 2
<続く>

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

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

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

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

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

guest

回答1

0

ベストアンサー

Thread and/or LinkedBlockingQueueの機能について勘違いされていると思います。

Threadというのは「ブロックされていない限り勝手に先にすすむもの」です。勝手に先にすすむというのは曖昧な言い方ですが、少し細かく言えば「OSによってプロセッサが割り当てられればいつでも処理は進む」とでもいいましょうか。通常OSにはPCに搭載されたプロセッサ(例えば2コアなら4プロセッサ)の数よりずっと多いスレッドが動いていますのでOSがいつどのスレッドにプロセッサを割り当てるかはその時々の状況次第なのでどのスレッドがいつ動くかの厳密な予測はできないと思ってください。

ただし最初にいったとおり「ブロックされていれば絶対に先には進みません」そういうときはOSが「このスレッドは先に進めないのだからプロセッサを割り当てても無駄」と判断して別の(ブロックされていない)スレッドにプロセッサを割り当ててしまいます。

このプログラムでいえばスレッドのブロックはLinkedBlockingQueueに対する操作で発生します。その発生条件は以下のようなものです。

  • offerしようとしたが、キューがいっぱいのためそれ以上offerできない場合
  • pollしようとしたが、キューが空のために取り出せない場合

以上を考えればoffer側で「3つoffserされてから」poll側のスレッドが動き出して「3つpollされる」といったような動きにはならないということがわかると思います。offer側が最初の要素をofferしたとたんにpoll側スレッドのブロック状態は解除され「いつでも動ける状態」になりますのでOSがプロセッサを割り当てた瞬間に動き出します。

整理すると以下のようになりますので、キューの要素数が1,2であればoffer, pollどちらのスレッドが先に動くかは状況次第になります。

  • offer側のスレッド

キューの要素数が3でない限りいつでも動けます

  • poll側のスレッド

キューの要素数が0でない限りいつでも動けます

実行結果の4,5行目でpollで要素を2つ削除しているのに6行目でofferのあと、要素数が3

以下の回答は回答者の勘違いでした: 失礼しました
単に勘違いされているだけでしょうが、要素が2の状態でofferが動くときのご自分のコードをご覧ください。要素を追加した後でキューのサイズを印字していますね?ゆえに要素数が2のときにofferが動けば印字結果が3になることに不思議はないはずです。

訂正した回答:
デバッグプリントもまたスレッド上で動いている処理の一つですのでどのような順序で動くかは状況次第であることに注意すると何が起こっているかが推測できると思います。例えば以下の順序で動いたとするとご質問にある結果になり得ると思います。

  • 要素数3の状態でpollが実行され要素数が3->2に変わった(A)
  • (A)のデバッグプリントが印字された
  • 要素数2の状態でofferが実行され要素数が2->3に変わった(B)
  • 要素数3の状態でpollが実行され要素数が3->2に変わった(C)
  • (C)のデバッグプリントが(先に)印字された
  • (B)のデバッグプリントが(ようやくここで)印字された

投稿2017/03/16 06:21

編集2017/03/16 09:24
KSwordOfHaste

総合スコア18394

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

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

Matt

2017/03/16 06:54

ご回答ありがとうございます。offer,pollが3連続にならないのは理解できました。 4、5行目の処理が終わった時点で、2つ要素を削除しているので、キューの要素数は1であり、6行目のofferでは2が表示されると思うのですが…。
KSwordOfHaste

2017/03/16 07:13

失礼しました、これは自分の勘違いです。回答を書き直します。
Matt

2017/03/16 07:45

納得できました。スレッド処理は1行単位で切り替わるのですね。 丁寧に解説してくださり、ありがとうございました。
yohhoy

2017/03/16 08:37

少し気になったので:「スレッド処理は1行単位で切り替わるのですね。」→いいえ。切り替わりません。KSwordOfHasteさん回答中にもある通り、スレッドをいつ切り替える(*)かはOSやJVMが勝手に判断します。 *厳密には「切り替える」という表現も適切ではないですが、分かり易さのためそのまま使いました。
KSwordOfHaste

2017/03/16 09:45

yohhoyさん補足ありがとうございます。 マルチスレッドでいろいろとプログラミングしていれば早晩気づくことになりますが、あるスレッドからのデバッグプリントの行の途中で別のスレッドからのデバッグプリントが出力されてしまい「ありゃりゃ」となることもありえます。行ごとにお行儀よく並ぶかのように見える理由はおそらくPrintStream#printlnが改行文字を出力した際にflushするという特徴に起因するのでしょう。yohhoyさんが示唆しておられるとおり「デバッグプリントが1行ごとに整然と出力されるとは限らない」と考えた方が無難と思います。>Mattさん
Matt

2017/03/26 07:19

補足いただき、ありがとうございます。
swordone

2017/03/26 15:38

JDKのPrintStream#printlnのコードは次のようになっています(引数intの例)。 public void println(int x) { synchronized (this) { print(x); newLine(); } } 内部ではsynchronizedされています。行ごと並ぶのはこのせいでしょう。
KSwordOfHaste

2017/03/26 15:56

おーなるほど! とはいえ行ごとに並ぶには条件(例えば同一のPrintStreamの出力に限るとか)があるので「常に行が混在しない」と考えるよりは、「大抵は大丈夫だがいつもそうなるとは限らない」ぐらいに認識しておくほうがよいでしょうね。実際System.out/System.errは環境によってタイミングがずれたりするのは普通にありますし。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問