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

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

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

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

Q&A

解決済

3回答

1362閲覧

Java スレッドの実行結果の違いについての質問

kazu0630

総合スコア26

Java

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

0グッド

1クリップ

投稿2020/04/05 11:56

現在、OCJPのGold資格の勉強を行っております。

それに伴い、スレッドの学習のためにコードを書いていたところ、以下の2つのコードで実行結果が異なることに対して、疑問に思いました。

実行結果が異なる理由をご教示願いたいです。

Java

1import java.util.concurrent.ExecutorService; 2import java.util.concurrent.Executors; 3 4public class Main { 5 6 public static void main(String[] args) { 7 ExecutorService service = null; 8 service = Executors.newSingleThreadExecutor(); 9 System.out.println("service.execute"); 10 String str = " * "; 11 for (int i = 0; i < 3; i++) { 12 service.execute(() -> { 13 System.out.print("thread task"); 14 for (int a = 0; a < 5; a++) { 15 System.out.print(str); 16 } 17 System.out.println(); 18 }); 19 } 20 } 21} 22 23■実行結果 24service.execute 25thread task * * * * * 26thread task * * * * * 27thread task * * * * *

Java

1 2import java.util.concurrent.ExecutorService; 3import java.util.concurrent.Executors; 4 5public class Main { 6 7 public static void main(String[] args) { 8 ExecutorService service = null; 9 service = Executors.newSingleThreadExecutor(); 10 System.out.println("service.execute"); 11 String str = " * "; 12 for (int i = 0; i < 3; i++) { 13 System.out.print("thread task"); 14 service.execute(() -> { 15 16 for (int a = 0; a < 5; a++) { 17 System.out.print(str); 18 } 19 System.out.println(); 20 }); 21 } 22 23 } 24 25} 26 27■実行結果 28service.execute 29thread taskthread taskthread task * * * * * 30 * * * * * 31 * * * * * 32 33 34

以上、宜しくお願い致します。

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

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

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

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

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

guest

回答3

0

System.out.print("thread task");

ってのがスレッドに入ってないので先に実行された、ってだけのことかと。

投稿2020/04/05 12:11

y_waiwai

総合スコア87719

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

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

kazu0630

2020/04/05 12:17 編集

ご回答いただき、ありがとうございます。 2つあるコードのうち、下のコードは以下のようになると想定しておりました。 ①外側のループ開始(3回繰り返す) ②System.out.print("thread task");で出力 ③スレッドが実行 ④内側のループ開始(* * * * * を出力) ⑤「②~④」を繰り返す 上記のようにならない理由はなんでしょうか。 「"thread task"」がスレッドよりも先に3回実行された後に、スレッドが3回実行されている理由がわかりません。
kazu0630

2020/04/05 12:27 編集

つまり、スレッドを使用した場合、呼び出した時点ですぐに実行されるわけではないないため、「"thread task"」が3回連続出力されたということでしょうか。 スレッドを使用した場合、呼び出した順番に実行はされないという理解でよろしいでしょうか。
kazu0630

2020/04/05 12:44 編集

せっかくURLをいただきましたが、スレッドに関する理解が浅く、理解することができませんでした。(どこを見ればよいのかがわかりませんでした)
shiketa

2020/04/05 13:00 編集

> 現在、OCJPのGold資格の勉強を行っております。 がんばってください。
y_waiwai

2020/04/05 13:15

スレッドってのは実行のタイミングは不定です。 先に登録したスレッドが先に実行されるという保証はありません また、登録したスレッドが即実行される保証もないです #気がついたらコメントが沢山
kazu0630

2020/04/05 13:31

mainメソッドの処理も1つのスレッドという認識なのですが、そちらは実行タイミングは不定ではないのでしょうか。 #いろいろな方に手助けしていただき、非常にありがたいです。
y_waiwai

2020/04/05 13:42

それはそのとおりです。 ましかし、この問題上では、mainメソッド実行中の話ですから、考慮に入れる必要はないです 不定、ということは、スレッドが登録即実行されても文句は言えず、 後者のコードで、前者の実行結果が得られる、可能性もあるということです。 #そして、どちらの結果でもない結果が得られるのかも
y_waiwai

2020/04/05 13:46

スレッドの実行タイミングは不定ですが、コードの実行順序はきっちり決まってますんで、 "thread task" の前に * が出力されることはないです
kazu0630

2020/04/05 13:58

ご回答いただき、ありがとうございます。 理解が深まりました。 もう少し手を動かして理解を深めていきたいと思います。 皆様、多くのコメントでのご支援、ありがとうございました。
guest

0

System.out.print("thread task");の位置ですね。

diff

1d$ diff --context=1 src/main/java/*/Main.java 2*************** 3*** 12,15 **** 4 for (int i = 0; i < 3; i++) { 5 service.execute(() -> { 6- System.out.print("thread task"); 7 for (int a = 0; a < 5; a++) { 8--- 12,15 ---- 9 for (int i = 0; i < 3; i++) { 10+ System.out.print("thread task"); 11 service.execute(() -> { 12 for (int a = 0; a < 5; a++) {

投稿2020/04/05 12:18

shiketa

総合スコア3971

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

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

0

ベストアンサー

2番目のコードが、メインスレッドでthread taskって3回出力してからサブスレッドが実行されているだけ、というオチな気がする。

投稿2020/04/05 12:13

gentaro

総合スコア8949

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

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

kazu0630

2020/04/05 12:23 編集

ご回答いただき、ありがとうございます。 スレッドについて、理解が浅いということもあり、なぜこのような事象になっているのかがわかっておりません。 2つあるコードのうち、下のコードは以下のようになると想定しておりました。 ①外側のループ開始(3回繰り返す) ②System.out.print("thread task");で出力 ③スレッドが実行 ④内側のループ開始(* * * * * を出力) ⑤「②~④」を繰り返す 上記のようにならない理由はなんでしょうか。 「"thread task"」がスレッドよりも先に3回実行された後に、スレッドが3回実行されている理由がわかりません。 メインスレッドとサブスレッドがある場合、ループ中でメインスレッドをループの回数分実行した後に、サブスレッドをループの回数分実行するといったルールがあるのでしょうか。
gentaro

2020/04/05 12:26

その想定はただの逐次処理であり、スレッド使う意味がありません。 スレッドを起動したらどのタイミングで実行されるかは不定です。
kazu0630

2020/04/05 12:34

ご回答いただき、ありがとうございます。 実行タイミングが不定というところをしっかりと意識できていれば分かる問題であったということですね。 それでは、何回か実行してみれば、例えば「thread task」が2回出力された後に、「* * * * * 」を出力するといったケースもでてくるということでしょうか。 ※今の所、何回か実行してみましたが、上記のケースはありません。
shiketa

2020/04/05 12:47

`Executors.newSingleThreadExecutor()`だからではないでしょうか。
kazu0630

2020/04/05 12:54

度々ご回答いただき、ありがとうございます。 マルチスレッドではないため、メインスレッドを3回実行してからサブスレッドを3回実行するという動きになっているということでしょうか。
shiketa

2020/04/05 13:01

いろいろあるので、違いをかんじてみるのもいいかもしれません。 ExecutorService service = null; service = Executors.newSingleThreadExecutor(); service = Executors.newWorkStealingPool(); service = Executors.newCachedThreadPool(); service = Executors.newFixedThreadPool(100); .... service.execute(() -> { System.out.print(Thread.currentThread());
gentaro

2020/04/05 13:01

メインスレッドで 「thread task出力」→「スレッド起動指示(未実行)」 「thread task出力」→「スレッド起動指示(未実行)」 「thread task出力」→「スレッド起動指示(未実行)」 が終わった後に 「起動待ちのひとつのスレッドを実行」→「終了」 「起動待ちのひとつのスレッドを実行」→「終了」 「起動待ちのひとつのスレッドを実行」→「終了」 ってなってるだけだと思いますが。
kazu0630

2020/04/05 13:07 編集

何度もご回答頂いているのに、なかなか理解できず申し訳ありません。 スレッド起動指示で実行せずに未実行となっている理由は、マルチスレッドではないからという認識でよろしいでしょうか。 今回のケースだと、メインスレッドが終わらないと、サブスレッドを実行できないということでしょうか。
gentaro

2020/04/05 13:12

逆。 マルチスレッドだから、サブスレッドの開始を命令してもメインスレッドの処理は動き続けるだけ。
gentaro

2020/04/05 13:23

すげー雑に言えば、メインスレッドがあなたで、サブスレッドは部下だとして、あなたは部下を1人もらい(生成し)ました。 あなたは部下に「*を5回出力して」と依頼しました。 あなたは部下に依頼しただけで、部下は準備ができたら仕事を開始します。 あなたは部下に依頼をしたら手が空くので、続きの仕事ができます。 あなたは部下に「*を5回出力して」と依頼しました。 あなたは部下に依頼しただけで、部下は準備ができたら仕事を開始します。 あなたは部下に依頼をしたら手が空くので、続きの仕事ができます。 あなたは部下に「*を5回出力して」と依頼しました。 あなたは部下に依頼しただけで、部下は準備ができたら仕事を開始します。 あなたは部下に依頼をしたら手が空くので、続きの仕事ができます。 あなたの仕事が終わりました。 部下が仕事をはじめました。部下が仕事を終了しました。 部下が仕事をはじめました。部下が仕事を終了しました。 部下が仕事をはじめました。部下が仕事を終了しました。 という事が起きてる。
kazu0630

2020/04/05 13:29

ご回答いただき、ありがとうございます。 お陰様でイメージができてきました。 いただいた上記のイメージでは、メインスレッドの仕事が終わった後に、サブスレッドが仕事を始めています。 メインスレッドの仕事が終わっていない間にサブスレッドが仕事を開始する準備できたら仕事を開始できるのでしょうか。
gentaro

2020/04/05 13:32

ループが終わった後にメインスレッドでなんか時間のかかる処理をやってみればわかるんじゃ無いですか。
kazu0630

2020/04/05 13:47 編集

以下のようにsleepを入れたら、メインスレッド→サブスレッド→メインスレッド→サブスレッドの実行順序になりました。 ということは、メインスレッドのすべての処理が完了する前にサブスレッドの実行準備が完了しなかった状態だったということがわかりました。 大分理解が深まりましたので、後は、自分でもう少し手を動かして理解を深めてみたいと思います。 import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { ExecutorService service = null; service = Executors.newSingleThreadExecutor(); System.out.println("service.execute"); String str = " * "; for (int i = 0; i < 3; i++) { System.out.print("thread task"); service.execute(() -> { for (int a = 0; a < 5; a++) { System.out.print(str); } System.out.println(""); }); try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } } } } ちなみに、ついでのお話なのですが、スレッドを使う場面がイメージができておりません。 スレッドはどういった場面で使うべきなのでしょうか。
gentaro

2020/04/05 14:11

スレッドの用途はそのまま「何か裏でやりたい場面」です。 一番わかりやすいのがGUIアプリで、メインスレッドではCPUの空いた時間に画面の描画処理が随時実行されますが、このメインスレッド上で別の重たい処理を書いてしまうと画面を再描画するタイミングがなくなってしまい、よく「画面が固まった」という現象が発生します。 これを防ぐために重い処理を別スレッドで実行したりします。 よく実感されたと思いますが、マルチスレッドはプログラミングの中でも特に難しい部類のテーマで、複雑なことをやるとすぐに処理フローを理解するのが困難になり、またバグったときの原因追求が大変だったりもします。 なので、いま難しいと感じていて、特段用途が思いつかないなら、必要性を感じるまでは他のことを優先して学習したほうが良いかもしれません。
kazu0630

2020/04/05 14:18

ご回答頂き、ありがとうございます。 スレッドの使用場面は、なんとなくですが、わかりました。 この先、いろいろな経験をしていく中で、スレッドへの理解を深めていけたらと思います。 お陰様でスレッドの学習をすすめる上で、1歩、2歩と前に進むことができました。 ここまでお付き合いいただき、ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問