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

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

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

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

Q&A

解決済

5回答

4201閲覧

マルチスレッドによる計算

snowman

総合スコア25

Java

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

0グッド

0クリップ

投稿2017/05/18 08:19

以下のようにマルチスレッドを使ってある数に1を永遠とたすプログラムを組んだのですが、スレッドが切り替わるたびに計算結果がずれてしまいます・・・
原因と解決方法を教えてください・・・

java

1 2public class MessyCounter extends Thread { 3 4 static int counter; 5 private int id; 6 MessyCounter(int id) { 7 this.id = id; 8 } 9 public synchronized void run() { 10 while (true) { 11 System.out.println(id + ":" +counter); 12 counter++; 13 } 14 } 15 16 public static void main(String[] args) { 17 // TODO Auto-generated method stub 18 Thread t[]=new Thread[4]; 19 for(int i=0;i<4;i++){ 20 t[i]=new Thread(new MessyCounter(i)); 21 } 22 t[0].start(); 23 t[1].start(); 24 t[2].start(); 25 t[3].start(); 26 27 } 28 29} 30

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

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

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

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

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

yohhoy

2017/05/18 13:07

そもそも、どういう結果を望んでいる(望ましい実行結果)のでしょうか?
snowman

2017/05/18 14:03

コンソールに表示されるcounterの値がしっかりと数字順に並ぶのが望ましい実行結果です。
guest

回答5

0

ベストアンサー

Before

java

1public synchronized void run() { 2 while (true) { 3 System.out.println(id + ":" + counter); 4 counter++; 5 } 6}
After

java

1public void run() { 2 while (true) { 3 displayAndIncrement(id); 4 } 5} 6 7private static synchronized void displayAndIncrement(int id) { 8 System.out.println(id + ":" + counter); 9 counter++; 10}

修正前のコードでは、MessyCounterクラスのインスタンスごとにrun()メソッドを同期化していることになります。
https://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6

For an instance method, the monitor associated with this (the object for which the method was invoked) is used.

そのため、ご提示のコードのように複数のスレッドが別々のインスタンスのrun()メソッドからcounterフィールドにアクセスした場合、
同期化されていないのと同じ結果になります。

一方、私の回答のように 同期化された staticメソッドからcounterフィールドにアクセスするようにすると、
MessyCounterクラスの Classオブジェクトによって同期化されるため、意図通りの動作になります。
上と同じリンク

For a class (static) method, the monitor associated with the Class object for the method's class is used.

投稿2017/05/18 09:14

KiyoshiMotoki

総合スコア4791

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

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

0

スレッドが切り替わるたびに計算結果がずれてしまいます・・・
コンソールに表示されるcounterの値がしっかりと数字順に並ぶのが望ましい実行結果です。

(1)マルチスレッド処理に関する大きな誤解と、(2)Java言語による実装上の問題 があります。


(1) 一言で言ってしまえば、マルチスレッド・プログラムはあなたの考える通りには動きません。

「各スレッドの処理が同じ早さで均等に進む」「ラウンドロビン方式で1ステップづつ処理される」ことを期待されているようですが、マルチスレッド処理の一般論として、そのような実行結果になることはありません。

今回の掲示プログラムで言えば、4つのスレッドがそれぞれ変数counterをインクリメント/値を参照する タイミングを予測することは不可能 です。そのため、ありとあらゆる実行結果が起こりえます。


(2) Java言語では、通常のメンバ変数を複数スレッドから 同時にアクセス(読み/更新) すると、予期しない結果をもたらすことがあります。他回答にある「排他制御/同期化が必要」という指摘は、このような同時アクセスを禁止するための手段です。

投稿2017/05/19 00:43

yohhoy

総合スコア6191

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

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

0

Java

1public class MessyCounter extends Thread { 2 private static int counter; 3 private static final Object locker = new Object();//ロック用オブジェクト 4 private final int id;// finalを追加 5 6 MessyCounter(int id) { 7 this.id = id; 8 } 9 10 public void run() { 11 while (true) { 12 synchronized (locker) { 13 System.out.println(id + ":" + counter); 14 counter++; 15 } 16 } 17 } 18 19 public static void main(String[] args) { 20 Thread t[] = new Thread[4]; 21 for (int i = 0; i < t.length; i++) { 22 // MessyCounterはThreadを継承しているため、new Threadを削除。 23 t[i] = new MessyCounter(i); 24 } 25 for (Thread thr : t) { 26 thr.start(); 27 } 28 } 29 30}

投稿2017/05/18 09:23

umyu

総合スコア5846

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

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

0

runメソッドを「synchronized」にしているのに、排他的にcounterが加算されないことが疑問点でしょうか?どのオブジェクトについてロックを取得しようとしているかを考察いただけますと、解決の手掛かりになるかと思います。

投稿2017/05/18 09:10

Bongo

総合スコア10807

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

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

snowman

2017/05/18 14:00

すみません、こういう指示の課題を出され、スレッドについての説明もまともにないまま「あとは自分で調べるなりなんなりして頑張れ」といった感じで丸投げされてしまったためロックの仕様についてしっかりと把握していません・・・。 なのでrunについた「synchronized」については「こんな感じかな・・・?」と思い適当につけて見た次第です・・・。
Bongo

2017/05/18 15:22 編集

なんと、放任主義的な先生のようですね。 「排他制御」「マルチスレッド」などのキーワードで検索しますと、今回の課題の参考になりそうな記事が色々見つかるかと思います。言語がJavaではないものも多いかと思いますが、こういった競合回避のための方法に大きな違いはないはずです。いろいろなサイトをご覧になれば、概念の理解が進むと思います。
guest

0

スレッドは、一つ一つ処理速度が違います。
なので、counterをアクセス時に他スレッドで足された後にアクセスするケースがズレにあたると思われます。

逆に質問しますが、同時に[0][1][2][3]が同じ値のcounterを取得するということでいいんでしょうか?
それとも[0] = 0, [1] = 1, [2] = 2, [3] = 3, [0] = 4, [1] = 5.....といったかんじでしょうか?

無理やりずらしてみました。

public class StartApp extends Thread { static int counter; private int id; StartApp(int id) { this.id = id; } public synchronized void run() { while (true) { System.out.println(id + ":" + counter); counter++; try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO 自動生成された catch ブロック e.printStackTrace(); } } } public static void main(String[] args) { // TODO Auto-generated method stub Thread t[] = new Thread[4]; for (int i = 0; i < 4; i++) { t[i] = new Thread(new StartApp(i)); } try{ t[0].start(); Thread.sleep(100); t[1].start(); Thread.sleep(100); t[2].start(); Thread.sleep(100); t[3].start(); Thread.sleep(100); } catch (InterruptedException e){ } }

各スレッドの生成を遅らせて生成を試みてみました。
正直無理やり感がぬぐえないのとテスト回数が少ないので正しいかどうかはわかりませんが一応貼っておきます。

投稿2017/05/18 08:44

編集2017/05/19 03:58
shafi_bo

総合スコア20

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

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

snowman

2017/05/18 13:56

[0] = 0, [1] = 1, [2] = 2, [3] = 3, [0] = 4, [1] = 5.....といった感じです!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問