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

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

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

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

並列処理

複数の計算が同時に実行される手法

同期

複数のディレクトリに存在するファイルを更新した場合に、すべてのファイルにも更新が行われる事、又は、同じ記憶領域に同時にアクセスして内容の整合性が失われてしまう事をを防ぐ制御などを同期と呼びます。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

Q&A

解決済

1回答

1609閲覧

スレッドの仕組みを使い、キャラクター同士で戦わせるプログラムでHPが0になっても処理が続いてしまう。

remontaruto8325

総合スコア3

Java

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

並列処理

複数の計算が同時に実行される手法

同期

複数のディレクトリに存在するファイルを更新した場合に、すべてのファイルにも更新が行われる事、又は、同じ記憶領域に同時にアクセスして内容の整合性が失われてしまう事をを防ぐ制御などを同期と呼びます。

非同期処理

非同期処理とは一部のコードを別々のスレッドで実行させる手法です。アプリケーションのパフォーマンスを向上させる目的でこの手法を用います。

コードレビュー

コードレビューは、ソフトウェア開発の一工程で、 ソースコードの検査を行い、開発工程で見過ごされた誤りを検出する事で、 ソフトウェア品質を高めるためのものです。

0グッド

0クリップ

投稿2021/12/09 17:59

編集2021/12/09 18:02

前提・実現したいこと

スレッドを使って、キャラクター同士で戦闘を行い、攻撃を仕掛ける度に攻撃を受けた側のHPをー1する。
どちらかのキャラクターのHPが0になったらその時点で処理をやめる。
HPが0になったキャラクターは攻撃してはならず、攻撃側もHP0のキャラを攻撃できない。

発生している問題・エラーメッセージ

どちらかのHPが0になっても、1回だけ死んだはずのキャラが攻撃してしまう。

実際の実行結果

Rock attacked Jay , Rock:10 , Jay:9
Rock attacked Jay , Rock:10 , Jay:8
Jay attacked Rock , Jay:8 , Rock:9
Rock attacked Jay , Rock:9 , Jay:7
Jay attacked Rock , Jay:7 , Rock:8
Rock attacked Jay , Rock:7 , Jay:6
Jay attacked Rock , Jay:6 , Rock:7
Jay attacked Rock , Jay:6 , Rock:6
Rock attacked Jay , Rock:6 , Jay:5
Rock attacked Jay , Rock:6 , Jay:4
Rock attacked Jay , Rock:6 , Jay:3
Jay attacked Rock , Jay:3 , Rock:5
Rock attacked Jay , Rock:5 , Jay:2
Jay attacked Rock , Jay:2 , Rock:4
Rock attacked Jay , Rock:4 , Jay:1
Jay attacked Rock , Jay:1 , Rock:3
Jay attacked Rock , Jay:1 , Rock:2
Rock attacked Jay , Rock:2 , Jay:0
Jay attacked Rock , Jay:0 , Rock:1
↑でJayは死んでいるはずなのに攻撃している。
Jaylost.

該当のソースコード

Java

1class Fight{ 2 public static void main(String args[]) { 3 RPGCharacter c1 = null,c2 = null; 4 c1 = new RPGCharacter("Jay",10); 5 c2 = new RPGCharacter("Rock",10); 6 c1.setTarget(c2); 7 c2.setTarget(c1); 8 c1.start(); 9 c2.start(); 10 } 11} 12class RPGCharacter extends Thread{ 13 String name; 14 int hp; 15 RPGCharacter target; 16 Object lock = new Object(); 17 18 RPGCharacter(String name,int hp){ 19 this.name = name; this.hp = hp; 20 } 21 void setTarget(RPGCharacter target) {this.target = target;} 22 RPGCharacter getTarget(){synchronized(lock) {return target;}} 23 void attack() { 24 synchronized(lock) { 25 while(hp>0&&target.hp>0) { 26 try {Thread.sleep((int)(10*Math.random()));} 27 catch(InterruptedException ie) {} 28 29 if(target.hp>0)target.hp--; 30 System.out.println(name + " attacked " + target.name + " , " + name + ":" + hp + " , " + target.name + ":" + target.hp); 31 } 32 if(hp<=0) { 33 System.out.println(name + "lost."); 34 } 35 } 36 } 37 public void run() { 38 attack(); 39 } 40} 41

試したこと

synchronizedの書き方をいろいろ試したのですがどうもうまくいきません!c1とc2、同時に処理が行われているから、HPが0になった判定while(hp>0&&target.hp>0)を受ける前に攻撃の処理をしてしまうっていうことは何となくわかるのですが、、
同期処理についての理解が足りないのでしょうか?

補足情報(FW/ツールのバージョンなど)

Java初心者なので、質問内容等で不快にさせてしまったら申し訳ありません!

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

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

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

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

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

guest

回答1

0

ベストアンサー

ロックによる同期は、複数のスレッドが同じモノをロック・アンロックすることで機能するのであって、それぞれのスレッドがそれぞれのモノをロック・アンロックしていては、同期になりません。

死んでいるのに攻撃してしまうのは、 while ループがそう書かれているからでしょう。
while ループは
・hpチェック
・スリープ
・攻撃
を繰り返します。
スリープ中に攻撃を受けて hp が 0 にされても、攻撃してからでなければチェックは行われません。

もう少し言いますと、それぞれのキャラクターはそれぞれ自分の hp を管理すべきで、攻撃したからと言って相手の hp を減らす処理を攻撃した側が行うのは変ではないでしょうか。
例えば「攻撃を受けた場合に相手から呼ばれる」wasAttacked メソッドを作り、 c1 が c2 を攻撃したなら c1 が c2 の wasAttacked を呼びます。
wasAttacked は自身の hp を減らし、 0 になったら自分に interrupt を掛けます。
attacked メソッドは sleep が interrupted 例外を発したら while ループを抜けるようにすれば、 hp 0 で即スレッドは終了するでしょう。
相手が倒れたかを wasAttacked の戻り値で分かるようにすれば、攻撃した側も戦闘の終了が分かります。
ですので while の条件で hp をチェックする必要もなく、 !interrupted() だけで良いです。
そしてこの場合、ロックによる同期は必要ありません。


「ロックによる同期は必要ありません」と書きましたが、攻撃した後の表示で問題が有りそうですので、ロックを入れました。

攻撃側が相手の wasAttacked を呼んでから hp を表示するまでの間に相手がこちらの wasAttacked を呼んでこちらの hp を減らし、その後こちらの表示が行われると、相手が攻撃した表示がされる前に hp が減っている状態になるからです。
つまり、相手の hp を変更してから(相手の hp を減らしたことを)表示するまでの間で(同時に表示している)自分の hp が変更されてしまうのを防ぐ為にロックします。

デッドロックが発生するため、 lock オブジェクトを static にしました。
lock の取り合いで負けた場合に倒される可能性があるため、ロック取得後に割り込みチェックを入れました。

java

1package teratail_java.q373120; 2 3public class Fight { 4 public static void main(String args[]) { 5 RPGCharacter c1 = new RPGCharacter("Jay", 10); 6 RPGCharacter c2 = new RPGCharacter("Rock", 10); 7 c1.setTarget(c2); 8 c2.setTarget(c1); 9 c1.start(); 10 c2.start(); 11 } 12} 13 14class RPGCharacter extends Thread { 15 private static Object[] lock = new Object[0]; 16 17 private String name; 18 private int hp; 19 private RPGCharacter target; 20 21 RPGCharacter(String name, int hp) { 22 this.name = name; 23 this.hp = hp; 24 } 25 26 void setTarget(RPGCharacter target) { 27 this.target = target; 28 } 29 30 void attack() { 31 while(!interrupted()) { 32 try { 33 Thread.sleep((long) (10 * Math.random())); 34 } catch(InterruptedException ie) { 35 break; //倒された 36 } 37 38 synchronized(lock) { 39 if(interrupted()) break; //倒された 40 boolean down = target.wasAttacked(); 41 System.out.println(name + " attacked " + target.name + " , " + this + " , " + target); 42 if(down) break; //倒した 43 } 44 } 45 46 if(hp <= 0) { 47 System.out.println(name + " lost."); 48 } 49 } 50 51 @Override 52 public void run() { 53 attack(); 54 } 55 56 boolean wasAttacked() { 57 if(--hp > 0) return false; //まだ倒れない 58 interrupt(); 59 return true; //倒れた 60 } 61 62 @Override 63 public String toString() { 64 return name + ":" + hp; 65 } 66}

plain

1Jay attacked Rock , Jay:10 , Rock:9 2Rock attacked Jay , Rock:9 , Jay:9 3Jay attacked Rock , Jay:9 , Rock:8 4Jay attacked Rock , Jay:9 , Rock:7 5Rock attacked Jay , Rock:7 , Jay:8 6Jay attacked Rock , Jay:8 , Rock:6 7Rock attacked Jay , Rock:6 , Jay:7 8Jay attacked Rock , Jay:7 , Rock:5 9Jay attacked Rock , Jay:7 , Rock:4 10Rock attacked Jay , Rock:4 , Jay:6 11Jay attacked Rock , Jay:6 , Rock:3 12Rock attacked Jay , Rock:3 , Jay:5 13Rock attacked Jay , Rock:3 , Jay:4 14Jay attacked Rock , Jay:4 , Rock:2 15Rock attacked Jay , Rock:2 , Jay:3 16Jay attacked Rock , Jay:3 , Rock:1 17Rock attacked Jay , Rock:1 , Jay:2 18Rock attacked Jay , Rock:1 , Jay:1 19Rock attacked Jay , Rock:1 , Jay:0 20Jay lost.

投稿2021/12/09 18:21

編集2021/12/10 09:43
jimbe

総合スコア12648

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

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

remontaruto8325

2021/12/10 14:13

とても分かりやすい解説ありがとうございました!無事望み通りの実行結果が出せました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問