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

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

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

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

Q&A

解決済

1回答

1536閲覧

synchronizedを元のプログラムに入れたい

nn.nn

総合スコア3

Java

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

0グッド

0クリップ

投稿2021/06/15 13:45

前提・実現したいこと

下のプログラムはタートルが衝突を検知するとその場で止まるというプログラムであるが、このプログラムは実行しているとすり抜けてしまう。
そこでsynchronizedを用いてプログラムを正常に動くようにしたい。

該当のソースコード

java

1public class TurtleCollision extends TurtleFrame { 2 public void start() { 3 Turtle m1 = new Turtle(50, 200, 90), m2 = new Turtle(350, 200, 270); 4 this.add(m1); 5 m1.speed((int)(Math.random() * 20.0) + 10); 6 this.add(m2); m2.tColor = Color.DEEPPINK; 7 m2.speed((int)(Math.random() * 20.0) + 10); 8 ExecutorService executor = Executors.newCachedThreadPool(); 9 final double step = 10.0; // 步幅 10 executor.execute(()-> { 11 while (m1.getX() < 400){ 12 if (0 == Double.compare(m1.getX() + step, m2.getX())) { // 衝突チェック 13 System.out.println("m1:collision at " + m1.getX()); 14 break; 15 } 16 m1.fd(step); // 1步進む 17 } 18 }); 19 executor.execute(()-> { 20 while (m2.getX() > 0){ 21 if (0 == Double.compare(m1.getX(), m2.getX() - step)) { // 衝突チェック 22 System.out.println("m2:collision at " + m2.getX()); 23 break; 24 } 25 m2.fd(step); // 1步進む 26 } 27 }); 28 } 29public static void main(String[] args) { 30 new TurtleCollision().start(); 31 } 32}

試したこと

synchronized void startSynchro()としてstart内のプログラムをコピーし、そこにsynchronizedを挿入して実行したが、うまくいかなかった。

java

1package prog2; 2import tg.*; 3import java.util.*; 4import java.util.concurrent.*; 5import javafx.scene.paint.Color; 6public class TurtleCollision extends TurtleFrame { 7 public void start() { 8 Turtle m1 = new Turtle(50, 200, 90), m2 = new Turtle(350, 200, 270); 9 this.add(m1); 10 m1.speed((int)(Math.random() * 20.0) + 10); 11 this.add(m2); m2.tColor = Color.DEEPPINK; 12 m2.speed((int)(Math.random() * 20.0) + 10); 13 ExecutorService executor = Executors.newCachedThreadPool(); 14 final double step = 10.0; // 步幅 15 executor.execute(()-> { 16 while (m1.getX() < 400){ 17 if (0 == Double.compare(m1.getX() + step, m2.getX())) { // 衝突チェック 18 System.out.println("m1:collision at " + m1.getX()); 19 break; 20 } 21 m1.fd(step); // 1步進む 22 } 23 }); 24 executor.execute(()-> { 25 while (m2.getX() > 0){ 26 if (0 == Double.compare(m1.getX(), m2.getX() - step)) { // 衝突チェック 27 System.out.println("m2:collision at " + m2.getX()); 28 break; 29 } 30 m2.fd(step); // 1步進む 31 } 32 }); 33 } 34 synchronized void startSynchro(){ 35 Turtle m1 = new Turtle(50, 200, 90), m2 = new Turtle(350, 200, 270); 36 this.add(m1); 37 m1.speed((int)(Math.random() * 20.0) + 10); 38 this.add(m2); m2.tColor = Color.DEEPPINK; 39 m2.speed((int)(Math.random() * 20.0) + 10); 40 ExecutorService executor = Executors.newCachedThreadPool(); 41 final double step = 10.0; // 步幅 42 synchronized(executor) { 43 executor.execute(()-> { 44 while (m1.getX() < 400){ 45 if (0 == Double.compare(m1.getX() + step, m2.getX())) { // 衝突チェック 46 System.out.println("m1:collision at " + m1.getX()); 47 break; 48 } 49 m1.fd(step); // 1步進む 50 } 51 }); 52 executor.execute(()-> { 53 while (m2.getX() > 0){ 54 if (0 == Double.compare(m1.getX(), m2.getX() - step)) { // 衝突チェック 55 System.out.println("m2:collision at " + m2.getX()); 56 break; 57 } 58 m2.fd(step); // 1步進む 59 } 60 }); 61 } 62 } 63public static void main(String[] args) { 64 new TurtleCollision().startSynchro(); 65 } 66}

このようにいれ、wait()なども試しましたが、一向にうまくいきません。どのようにsynchronizedを挿入すればいいのでしょうか

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

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

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

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

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

dodox86

2021/06/15 23:19

衝突を正しく検知したいためにsynchronizedを付けたいとのことですが、それで解決できる見込みがあるのでしょうか。
xebme

2021/06/17 09:41 編集

m1、m2が共有できるオブジェクトをsynchronizedしてください。<-- それほど容易ではないことがわかりました。衝突を判定するときに、m1,m2の内部状態がメモリに書き戻されていなければならないからです。synchronizedを使えば、L2キャッシュにデータがあったとしてもメモリ書き戻しが保証されます。
guest

回答1

0

ベストアンサー

すれ違いが起きる状況と解決方法

Turtleが1ステップ実行したあとに必ず衝突判定が行われるなら、すれ違いは起きません。衝突した直後の状況を考えてみます。

  • m1が1ステップ前進 (衝突)
  • m2が1ステップ前進 (すれ違い)
  • m1またはm2が衝突を判定 (衝突ではない)

2つのスレッドは自由に動けるのでこれが起こります。そこで、衝突判定と前進を分割不可分なセットにします。セットを排他的に実行できるよう、synchronizedを使います。synchronized(<オブジェクト>) のオブジェクトは2つのスレッドが共有するオブジェクトです。

Java

1 synchronized(<共有オブジェクト>) { 2 if (衝突) メッセージを表示して終了; 3 前進; 4 }

メモリの同期

マルチコア・プラットフォームで、スレッドが別々のコアで実行すると、共有オブジェクトの更新結果がキャッシュに残り、メモリに書き戻されない状況が生まれます。他のコアで実行するスレッドはメモリの更新前の値を見ることになります。

m1、m2の座標は、前進によって更新されますが、2つのスレッドが衝突判定できるには、m1、m2の値が正しくメモリに書き戻されなければなりません。synchronized によってメモリへの書き戻しが保証されます。(vlatileは1変数のみアトミックに書き戻す)

synchronized/volatileのコストは高くつきます。

synchronizedを使わない解法

synchronizedを使わず、失敗させることで結果的に同期する方法があります。AtomicIntegerを使う解法を紹介します。

Java

1 // stepに換算したm2とm1の距離 2 final AtomicInteger stepsRemained = new AtomicInteger((int)((m2.getX() - m1.getX()) / step)); 3 4 executor.execute(()-> { 5 while (stepsRemained.updateAndGet(v -> (v == 0) ? 0 : --v ) > 0) m1.fd(step); // 1步進む 6 System.out.println("m1:collision at " + m1.getX()); 7 }); 8 9 executor.execute(()-> { 10 while (stepsRemained.updateAndGet(v -> (v == 0) ? 0 : --v ) > 0) m2.fd(step); // 1步進む 11 System.out.println("m2:collision at " + m2.getX()); 12 });

m1 、m2の2つの状態を管理するのは難しい。Turtleの距離だけをみるよう問題を単純化しました。

投稿2021/06/18 21:06

xebme

総合スコア1089

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.37%

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

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

質問する

関連した質問