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

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

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

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

Q&A

解決済

1回答

1315閲覧

Threadのjoin()で別スレッドを動かす前のプログラムが一部反映されない。

pea-cap

総合スコア4

Java

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

1グッド

0クリップ

投稿2021/06/22 12:48

編集2021/06/22 13:11

##javaでゲーム制作をしています。

今はゲーム画面の画面移行を制作中です。
画面の移行前に、画面が固まるようになってきたので
処理が終わるまでNow Roading画面を挟みたいと思い
ThreadとRunnableを試したりしています。

###追伸
Teratailの投稿ボタンを押す → 何故か投稿できず
そのまま再度投稿すると、かなり前の文章になってしまいました。(何故?)
その為、編集前の文章で見ていたらごめんなさい。

追伸しました。
タイトルも…前のになっていたので追伸
動かない → 一部反映されない

##試した事
ボタン1は画面移行をする前に待機パネル(白)を表示させて
別スレッドで処理させています。処理順でパネル(黒)になります。

ボタン2は、別スレッドで動かしている間に、メインスレッドのプログラムが
動くか試しています。待機パネル(白)、時間が掛かりパネル変更(緑)
になりました。

ボタン3はボタン2とは違い、join()によって別スレッドが終わるまで
待機してから(黄)最後に待機パネル(白)が表示されました。
(黄)は早すぎるのか見えないけど…
joinが原因かもしれません…

ボタン4は、join()メソッドによって別スレッドの処理が全部終わるまで
終了されないか試しました。

##実現したい事

ここまでは自分の思う挙動範囲でした。しかし

ボタン5は、start()メソッド手前で白に背景を変えているのに
何故か変更されませんでした。
Class Eの方のpinkを外すと
別スレッドが終了してから白背景になりました。

別スレッドが起動前だから、起動前にパネルは白になるはず…
と思っていたのですが…

待機画面を出現させて、全部読み込ませてから
背景含め表示させたいと思っているので困っています。

##試した事
大変申し訳ないのですが、スレッドについて勉強中です。
とても簡素にして、挙動その物が合っているか試したのが
下記ソースコードとボタン1~4です。

ゲームで待機画面を出現させて、全部読み込ませてから
背景含め表示させようとしたのがボタン5です。

①別スレッドでなら背景色が変更になるか試す。
別スレッド内に記載したがならなかった。
②JFrameがそもそも変更できるかsetSizeを試す。
joinなし → 拡大後、背景が白→黒になる。
joinあり → 拡大後、背景変わらず、拡大した部分は黒くなり
拡大した部分を含めて背景色がピンクになる。

joinが原因なのは分かりました。
しかし、何故joinがこのような挙動をするか分かりません。
また、解決方法も教えて頂けたらと思っています。

ソースコードです。

java

1import java.awt.Color; 2import java.awt.Dimension; 3import java.awt.event.ActionEvent; 4import java.awt.event.ActionListener; 5 6import javax.swing.JButton; 7import javax.swing.JFrame; 8import javax.swing.JPanel; 9 10public class TestThread extends JFrame implements ActionListener, Runnable { 11 JPanel gamePanel = new JPanel(); 12 public JPanel getGamePanel() {return gamePanel;} 13 14 TestThread tt; 15 private int tBtn = -1; 16 17 public TestThread() { 18 tt = this; 19 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 20 this.setSize(new Dimension(500, 500)); 21 this.setVisible(true); 22 23 JButton btn1 = new JButton("ボタン1"); 24 JButton btn2 = new JButton("ボタン2"); 25 JButton btn3 = new JButton("ボタン3"); 26 JButton btn4 = new JButton("ボタン4"); 27 JButton btn5 = new JButton("ボタン5"); 28 29 btn1.setActionCommand("btn1"); 30 btn2.setActionCommand("btn2"); 31 btn3.setActionCommand("btn3"); 32 btn4.setActionCommand("btn4"); 33 btn5.setActionCommand("btn5"); 34 35 btn1.addActionListener(this); 36 btn2.addActionListener(this); 37 btn3.addActionListener(this); 38 btn4.addActionListener(this); 39 btn5.addActionListener(this); 40 41 gamePanel.add(btn1); 42 gamePanel.add(btn2); 43 gamePanel.add(btn3); 44 gamePanel.add(btn4); 45 gamePanel.add(btn5); 46 this.add(gamePanel); 47 } 48 49 public static void main(String[] args) { 50 TestThread testT = new TestThread(); 51 } 52 53 @Override 54 public void run() { 55 if (tBtn == 1) { 56 System.out.println("1画面、Now Roading 別スレッドで起動"); 57 for (int i = 0; i < 1000000; i++) { 58 System.out.println("iの数を数えましょう:" + i); 59 } 60 A a = new A(this); 61 } 62 if (tBtn == 2) { 63 System.out.println("2画面、Now Roading 別スレッドで起動"); 64 for (int i = 0; i < 1000000; i++) { 65 System.out.println("iの数を数えましょう:" + i); 66 } 67 B b = new B(this); 68 } 69 if (tBtn == 3) { 70 System.out.println("3画面、Now Roading 別スレッドで起動"); 71 for (int i = 0; i < 1000000; i++) { 72 System.out.println("iの数を数えましょう:" + i); 73 } 74 C c = new C(this); 75 } 76 if (tBtn == 4) { 77 System.out.println("4画面、Now Roading 別スレッドで起動"); 78 for (int i = 0; i < 1000000; i++) { 79 System.out.println("iの数を数えましょう:" + i); 80 } 81 D d = new D(this); 82 } 83 if(tBtn == 5) { 84 // こっちでも試す 85 this.getGamePanel().setBackground(Color.WHITE); 86 System.out.println("5画面、Now Roading 別スレッドで起動"); 87 for (int i = 0; i < 1000000; i++) { 88 this.getGamePanel().setBackground(Color.WHITE); 89 System.out.println("iの数を数えましょう:" + i); 90 } 91 E d = new E(this); 92 } 93 } 94 95 @Override 96 public void actionPerformed(ActionEvent e) { 97 String cmd = e.getActionCommand(); 98 if (cmd.equals("btn1")) { 99 System.out.println("btn1が押されたよ"); 100 this.setSize(600,600); 101 tBtn = 1; 102 Thread t = new Thread(this); 103 this.getGamePanel().setBackground(Color.WHITE); // 待機パネル 104 t.start(); 105 } 106 if (cmd.equals("btn2")) { 107 System.out.println("btn2が押されたよ"); 108 tBtn = 2; 109 Thread t = new Thread(this); // 待機パネル 110 t.start(); 111 // 処理が終わる前に白背景になっている。 112 this.getGamePanel().setBackground(Color.WHITE); 113 } 114 if (cmd.equals("btn3")) { 115 System.out.println("btn3が押されたよ"); 116 tBtn = 3; 117 Thread t = new Thread(this); 118 t.start(); 119 try { 120 t.join(); 121 } catch (InterruptedException e1) { 122 e1.printStackTrace(); 123 } 124 // ↓をコメントアウトすると黄背景になる。 125 this.getGamePanel().setBackground(Color.WHITE); 126 } 127 if (cmd.equals("btn4")) { 128 System.out.println("btn4が押されたよ"); 129 tBtn = 4; 130 this.getGamePanel().setBackground(Color.WHITE); 131 Thread t = new Thread(this); 132 t.start(); 133 try { 134 t.join(); // 別スレッドが終わるまで待機。ないと終了される。 135 } catch (InterruptedException e1) { 136 e1.printStackTrace(); 137 } 138 System.exit(0); 139 } 140 if (cmd.equals("btn5")) { 141 System.out.println("btn5が押されたよ"); 142 tBtn = 5; 143 this.getGamePanel().setBackground(Color.WHITE); 144 this.setSize(600,600); 145 // ここで、t.start()される前にPanelが白になるはず……? 146 // なのにならない。 147 Thread t = new Thread(this); 148 t.start(); 149 try { 150 // 試しにこっちでも 151 this.getGamePanel().setBackground(Color.WHITE); 152 t.join(); 153 } catch (InterruptedException e1) { 154 e1.printStackTrace(); 155 } 156 } 157 } 158} 159 160class A extends JPanel { 161 public A(TestThread testThread) { 162 System.out.println("classAの画面起動しました。"); 163 testThread.getGamePanel().setBackground(Color.BLACK); 164 } 165} 166 167class B extends JPanel { 168 public B(TestThread testThread) { 169 System.out.println("classBの画面が起動しました。"); 170 testThread.getGamePanel().setBackground(Color.GREEN); 171 } 172} 173 174class C extends JPanel { 175 public C(TestThread testThread) { 176 System.out.println("classCの画面が起動しました。"); 177 testThread.getGamePanel().setBackground(Color.YELLOW); 178 } 179} 180 181class D extends JPanel { 182 public D(TestThread testThread) { 183 System.out.println("classDの画面が起動しました。"); 184 } 185} 186 187class E extends JPanel { 188 public E(TestThread testThread) { 189 System.out.println("classEの画面が起動しました。"); 190 testThread.getGamePanel().setBackground(Color.PINK); 191 } 192}

##補足情報
Eclipse Java8

TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

端的に言えば「joinしてしまったら、別スレッドにした意味がない。」ですが、ちょっと難しく考えすぎている気がします。

画面遷移中にローディング画面を出したいってだけですよね?

スレッド開始前にローディング画面にして、スレッドの最後に本来の遷移先を出せばいいのであって、別にjoinの出番はないと思うのですが。

Java

1import java.awt.Color; 2import java.awt.Dimension; 3import java.awt.Graphics; 4import java.awt.event.ActionEvent; 5import java.awt.event.ActionListener; 6import javax.swing.JButton; 7import javax.swing.JFrame; 8import javax.swing.JPanel; 9import javax.swing.JProgressBar; 10import javax.swing.SwingUtilities; 11 12public class TestThread extends JPanel implements ActionListener { 13 public static void main(String[] args) { 14 // 本来は main でも invokeLater すべき 15 // でもなくてもエラーになったことないんですよね^^; 16 SwingUtilities.invokeLater(() -> { 17 var frame = new JFrame(); 18 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 19 frame.setSize(500, 500); 20 frame.setLocationRelativeTo(null); 21 frame.add(new TestThread()); 22 frame.setVisible(true); 23 }); 24 } 25 26 JButton btn1 = new JButton("start"); 27 JButton btn2 = new JButton("start & join"); 28 JButton btn3 = new JButton("no thread"); 29 JProgressBar progress = new JProgressBar(); 30 31 public TestThread() { 32 btn1.addActionListener(this); 33 btn2.addActionListener(this); 34 btn3.addActionListener(this); 35 add(btn1); 36 add(btn2); 37 add(btn3); 38 39 progress.setIndeterminate(true); 40 progress.setPreferredSize(new Dimension(400, 30)); 41 progress.setVisible(false); 42 add(progress); 43 } 44 45 @Override public void paintComponent(Graphics g) { 46 super.paintComponent(g); 47 System.out.println("paintComponent"); 48 } 49 50 @Override public void actionPerformed(ActionEvent e) { 51 setBackground(Color.WHITE); 52 System.out.println("\nWHITE"); 53 54 if (e.getSource() == btn1) { 55 progress.setVisible(true); 56 Thread t = new Thread(() -> { 57 sleep(); 58 SwingUtilities.invokeLater(() -> { 59 setBackground(Color.BLACK); 60 System.out.println("BLACK"); 61 progress.setVisible(false); 62 }); 63 }); 64 t.start(); 65 } 66 67 if (e.getSource() == btn2) { 68 // progress.setVisible(true); // 出ません。 69 Thread t = new Thread(() -> { 70 sleep(); 71 // 「暇になった時にやっといてね」なので BLUE->GREEN になる 72 SwingUtilities.invokeLater(() -> { 73 setBackground(Color.GREEN); 74 System.out.println("GREEN"); 75 // progress.setVisible(false); 76 }); 77 78 // こうなら GREEN->BLUE だが別スレッドでUIをさわるとエラーになる(時がある)のでダメ! 79 // setBackground(Color.GREEN); 80 // System.out.println("GREEN"); 81 }); 82 t.start(); 83 try { 84 // join で待つので終わるまで下に行かずUIスレッドが止まる 85 // その間 paintComponent も呼ばれないし当然色も変わらない 86 // ボタンも押された状態のまま 87 t.join(); 88 } catch (InterruptedException ignored) { } 89 } 90 91 // join したら結局これと同じことになり、別スレッドにした意味がない 92 if (e.getSource() == btn3) { 93 // progress.setVisible(true); // 出ません。 94 sleep(); 95 setBackground(Color.PINK); 96 System.out.println("PINK"); 97 // progress.setVisible(false); 98 } 99 100 setBackground(Color.BLUE); 101 System.out.println("BLUE"); 102 } 103 104 private void sleep() { 105 try { 106 Thread.sleep(5000); 107 } catch (InterruptedException ignored) { } 108 } 109}

投稿2021/06/22 22:01

TN8001

総合スコア9396

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

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

pea-cap

2021/06/23 03:06

ご回答ありがとうございます! join()実装は、思っていた考えとズレてました。 実際には動かしながら、 別の処理をしておきたいので 仰る通りjoinはいらなかったです。 btn2の挙動が(青→緑)分からなくて SwingUtilities.invokeLater() -> Swingはシングルスレッドモデル、 イベントディスパッチスレッドとラムダ式と 全く分からなかったので勉強してきます。 (触りだけしてきました) と同時に、invokeLater()を用いての ローディング画面→画面移行のアイデアが 思い浮かびましたのでやってみます。 疑問があるのですが btn2,btn3は、(特にbtn3、スレッドも関係ないif文なのに) @Override public void actionPerformed(ActionEvent e){ setBackground(Color.WHITE); System.out.println("\nWHITE"); } 1行目の段階で背景色の変更(白)のメソッドが呼ばれているので 背景が白になってから 背景が変わると考えているのですが ボタンを押して5秒停止前に白になっていないのは何ででしょう…? 5秒停止後には白になるのですが(他の背景色の変更はコメントアウト) (start)だと、背景が白になった後 (setBackground(Color.BLUE);をコメントアウトしないと一瞬で見えないけど) 青になっているのは分かります。 色が変わる前に、sleepでスレッドが止まっていて sleep後に色が変わるから?と思って、 sleep前に setBackground(Color.WHITE); for(int i = 0; i < 1000000; i++) { System.out.println("white"); } sleep(); とか入れてみたのですが、変わらなかったです。
TN8001

2021/06/23 04:42

> 1行目の段階で背景色の変更(白)のメソッドが呼ばれているので > 背景が白になってから > 背景が変わると考えているのですが > ボタンを押して5秒停止前に白になっていないのは何ででしょう…? そのためにpaintComponentをオーバーライドしてprintlnしているのですが、わかりにくかったでしょうか?^^; setBackgroundしますが、実際に色を変える処理はsuper.paintComponentがやっています。 setBackgroundは色を変えるというより、色変更の予約をしたと考えてください。 actionPerformedはUIスレッドで動いています。 paintComponentもUIスレッドで呼ばれます。 つまりUIスレッドをフリーにしてあげないと、色が変わらないということです。 * btn1 WHITE BLUE paintComponent 5秒待ち BLACK paintComponent * btn2 WHITE 5秒待ち BLUE paintComponent GREEN paintComponent * btn3 WHITE 5秒待ち PINK BLUE paintComponent
pea-cap

2021/06/23 04:57

>actionPerformedはUIスレッドで動いています。 >paintComponentもUIスレッドで呼ばれます。 >つまりUIスレッドをフリーにしてあげないと、色が変わらないということです。 なるほどです! 理解できました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問