• ベストアンサー

ローカル変数にアクセスする複数のスレッドの振る舞いについて

早速ではございますが、質問をさせていただきます。 以下のコード中のMonitorクラスを複数のスレッドが 共有して自由に入り込んでいるにも関わらす、 排他制御をしていないのにBROKENと表示されません。 final class Main { private final static int NUM = 10; public static void main(String[] args) { Monitor m = new Monitor(); UserThread[] ut = new UserThread[NUM]; Thread[] t = new Thread[NUM]; for(int i=0;i<NUM;i++) { ut[i] = new UserThread(m); t[i] = new Thread(ut[i]); t[i].start(); } } } final class UserThread implements Runnable { private final Monitor m; public UserThread(Monitor m) { this.m = m; } public void run() { System.out.println (Thread.currentThread().getName()+" BEGIN"); while(true) { this.m.func(); } } } final class Monitor { public void func() { int i1 = 0; int i2 = 0; i1++; i2++; if(i1 != i2) { System.out.println ("***** BROKEN ***** " +i1+"と"+i2); } } } 動作確認を2日間くらい行っているのですが、それでも BROKENと表示してくれません。javaの処理系によって 振る舞いがことなるかもしれませんので一概には言えませ んが、いったいなぜBROKENと表示しないのでしょうか? どうかご教授のほどよろしくお願いします。

  • Java
  • 回答数9
  • ありがとう数2

質問者が選んだベストアンサー

  • ベストアンサー
回答No.9

CPUが複数あるコンピュータの場合は本当に複数のスレッドが本当に同時に動く場合があると思いますが、それでもローカル変数はメモリの中の違う所に入っているので絶対に取り違えることはありません。(あるとしたらそれは Java VM か OS のバグです)。

その他の回答 (8)

  • UKY
  • ベストアンサー率50% (604/1207)
回答No.8

> Thread-0が実行中のとき、Thread-1も実行中となるようなことが起きたとしても、Thread-0の処理自体にThread-1がその処理に入り込むことはないということでよろしいのでしょうか? 「入り込む」という言葉をどういう意味で使っているのか分からないので何とも……。 って言うか皆さん、コードの意味を定める言語レベルの話と実行時のメモリがどうとかいう VM レベルの話を一緒にするのはよろしくないと思いますが。

回答No.7

実際の動作で if の演算処理中に割り込みが掛かって他のスレッドに処理が移ることはあると思いますが、それでも問題なくスレッド毎に正常に動くようになっています。なぜそれが出来るのかというと、 i1, i2 などの変数がメモリ上のどこにあるのかという情報がスレッド単位でまとめて保存してあり、割り込みにより処理が切り替わる時にその情報を適切に適用しているためです。そのため他のスレッドのローカル変数へのアクセスは起こりません。

takeshix100
質問者

補足

___noboru___様へ 大変勉強になりました。誠にありがとうございました。 マルチスレッドの基本的で根本的なことをご質問させてい ただきたいのですが、 Thread-0が実行中のとき、Thread-1も実行中となるような ことが起きたとしても、Thread-0の処理自体にThread-1 がその処理に入り込むことはないということでよろしい のでしょうか?

回答No.6

Monitor#func() の最初のところはこうなってますが、 public void func() { int i1 = 0; int i2 = 0; この int i1 = 0; の所の処理は新たに int の変数のための領域を確保して 0 に初期化して以後のアクセスは i1 という名前で行う、です。(i2 も同様です)。 で、この処理が各スレッドごとに実行されます。つまりスレッドごとに i1, i2 のメモリの領域が違っています。違っているから他のスレッドに影響することはないんです。

takeshix100
質問者

補足

___noboru__様へ。 ご教授ありがとうございました。大変勉強になりました。 ローカル変数は、各スレッド毎に保持されていて、各スレ ッドごとに保持されているローカル変数の値は異なるのですね。実はローカル変数にアクセスする複数のスレッド の振る舞いについてもわからなかったのですが、その他に わからなかったことがありまして質問させていただきたいのですが、「1つのスレッドの処理に他のスレッドがその処理に割り込んで実行されるのではないか」ということが あり得るのかどうかがわかりません。例えば、Thread-0が Monitorクラスのif文の( )内を実行中にThread-0がi1に値を代入してi2にThread-1が割り込んできて値を代入 するようなことが起きるのかどうかがわかりません。 どうかご教授のほどよろしくお願い申し上げます。

noname#18558
noname#18558
回答No.5

>フィールドはメインメモリに格納されていて、 >各スレッドはメインメモリを共有していると思うのですが こんな話はきいたことがありませんが、どこの情報でしょうか? スレッドは個別にメモリを割り当てられ、共有はしません。 インスタンス変数と、メソッド変数を混同されてないでしょうか?

takeshix100
質問者

補足

DQ9様へ 返事が遅れまして申し訳ありませんでした。 著者名はわからないのですが、その参考書には、確か 「メインメモリは、インスタンスが存在する領域であり、 インスタンスが持っているフィールドはメインメモリにあります。個々のスレッドが持っている作業用のメモリを ワーキングメモリといい、ワーキングメモリには、メインメモリのうち必要な部分のコピー(例えばフィールドの値のコピー)が存在します。メソッドの引数やメソッド変数は1つのスレッドからしかアクセスされないので、ワーキングメモリ上にあると思ったほうが理解しやすい。」みたいなことが書かれていたことを読んだことがあります。

noname#18558
noname#18558
回答No.4

言葉だけで説明するのはすごく難しいんですが、 簡単に説明すると、メソッドの中は例えスレッドでも同期がとられています。 というか、別のプログラムが中に割り込むことができません。 スタックとか、メモリ領域のことが理解できていれば分かるのですが。

takeshix100
質問者

補足

DQ9様へ。 たびたび申し訳ありません。フィールドはメインメモリ に格納されていて、各スレッドはメインメモリを共有して いると思うのですが、メソッド変数は各スレッドに 割り当てられているメモリ領域に格納されていると、どこ かで聞いたことがあります。そのことと何か関連があるの でしょうか?

回答No.3

ちょっと横やりですが簡単に。 インスタンスメソッド内のローカル変数は、インスタンスの数だけ用意されます。 なので「複数のスレッドで共有」ということにはならないのです。

  • Bonjin
  • ベストアンサー率43% (418/971)
回答No.2

変数のスコープについてちゃんと理解されていないようです。 スコープは基本中の基本ですので参考書を読み直すなりしてちゃんと理解した方が良いでしょう。

takeshix100
質問者

補足

Bonjin様のご指摘の通りおかしなコードをさらしてしまい ました。申し訳ございませんでした。おかしなコードの部 分を修正して改めて質問をさせていただきます。 変更箇所はUserThreadクラスのrunメソッド内で無限ルー プを削除したところと、Monitorクラスに無限ループを 入れたところです。以下のコードが修正したコードです。 final class UserThread implements Runnable { private final Monitor m; public UserThread(Monitor m) { this.m = m; } public void run() { System.out.println (Thread.currentThread().getName()+" BEGIN"); this.m.func(); } } final class Monitor { public void func() { int i1 = 0; int i2 = 0; while(true) { i1++; i2++; if(i1 != i2) { System.out.println ("***** BROKEN ***** " +i1+"と"+i2); } } } } 何を質問したいのかというと、Monitorクラスの無限ループ 内で複数のスレッドがif文を実行するときにif文の( ) 内に複数のスレッドが入り込んでもBROKENと表示されない のはなぜなのかがわかりません。ローカル変数は、 複数のスレッドで共有するものではなく、各スレッドで 保持しているものであると認識しています。ですが、 if文の( )内に複数のスレッドが入り込むとBROKENと表示さ れそうな気がしてなりません。BROKENと表示されないのは なぜなのでしょうか?どうかご教授のほどよろしくお願い 申し上げます。

noname#18558
noname#18558
回答No.1

何億回実行しても、「BROKEN」とは表示されませんよ。 Monitor#func()のi1もi2もメソッド変数なので競合することはありません。 これをインスタンス変数として、メソッドの外に宣言すればなるでしょう。

takeshix100
質問者

補足

DQ9様へ 「Monitor#func()のi1もi2もメソッド変数なので競合することはありません。」というのは、メソッド変数という ものは、複数のスレッドで共有されるものではなくて、 たとえMonitorクラスのif文の( )内に複数のスレッド が自由に入り込んでいたとしても、各スレッドは自分が持 っているメソッド変数i1とi2によってif文の評価を行うという認識でよろしいのでしょうか?

関連するQ&A

  • スレッド間の送受信のコードについて

    下記のコードは、(以前お教え頂きました)『synchronized』を使用した、スレッド間の 送受信のコードですが、その代わりに、Interruptとか、Thread.sleep()を使用した、 コードに変更してみたいと思います。 Q1)そのコードをお教え頂けないでしょうか? class T_thread { public static void main(String args[]) { new rcvInterrupt().start(); } } class genInterrupt extends Thread { private rcvInterrupt target; genInterrupt(rcvInterrupt targetx) { this.target = targetx; } public void run() { System.out.println("genInterupt start"); for (int i = 0; i < 10; i++) { while (!target.isWaited) { // rcvInterruptがsynchronizedブロックに入るまで待ち合わせ // これがないと連続してsynchronizedしてしまう可能性がある } synchronized (target) { target.notifyAll(); target.isWaited = false; System.out.println("notify"); } } } } class rcvInterrupt extends Thread { volatile boolean isWaited; public synchronized void run() { new genInterrupt(this).start(); for (int i = 0; i < 10; i++) { try { isWaited = true; wait(); System.out.println("get interrupt cnt=" + i); } catch (InterruptedException e1) { } } } } 以上

    • ベストアンサー
    • Java
  • Threadに関するの質問

    このプログラムに synchronized , wait(), notify()を加えることで、 stackBody[]配列をオーバーやアンダーフローしないようにできるのでしょうか。お助け下さい! public class test { public static void main(String args[]) { Stack stack = new Stack(5); Pusher pusher = new Pusher(stack); Popper popper = new Popper(stack); pusher.start(); popper.start(); } } class Stack { private int stackSize; private int stackPointer; private int stackBody[]; public Stack(){ this(10); } public Stack(int size){ stackSize = size; stackBody = new int[size]; stackPointer = 0; } public void push(int value){ if(stackSize == stackPointer){ // } stackBody[stackPointer++] = value; } public int pop(){ if(stackPointer == 0){ // } return stackBody[--stackPointer]; } } class Pusher extends Thread { Stack s; public Pusher(Stack s) { this.s = s; } public void run() { int n, interval; for(int i = 0; i < 10; i ++) { n = (int)(100.0 * Math.random()); interval = (int)(100.0 * Math.random()); s.push(n); System.out.println("Pusher:" + n); try{ Thread.sleep(interval); }catch(InterruptedException e) {} } System.out.println("Push 終了"); } } class Popper extends Thread { Stack s; public Popper(Stack s) { this.s = s; } public void run(){ int n, interval; for(int i = 0; i < 10; i++){ interval = (int)(100.0 * Math.random()); n = s.pop(); System.out.println("Popper:" + n); try{ Thread.sleep(interval); }catch(InterruptedException e) {} } System.out.println("Popper 終了"); } }

    • ベストアンサー
    • Java
  • Javaのスレッドに関して質問です

    Assistantクラスを使い待機状態と再開を確認できるプログラムの作成 loafとrestartメソッドを設ける workメソッドが呼ばれる度loafを呼び出す Managerクラスを定義 checkメソッドを設け、Assistantをcheckし続ける loaf状態ならrestartさせる (Managerクラスはデーモンスレッド) ということなんですが、いまいちうまくいきません さぼっても復帰してくれません public class Assistant implements Runnable { private String name; private Chore c; public Assistant(String name, Chore c) { this.name = name; this.c = c; } public void run() { work(); } public void work() { while (true) { synchronized (c) { if (c.doEnd()) break; System.out.println(name + " : " + c.digest()); loaf(); } } } public synchronized void loaf() { try { c.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void restart() { c.notify(); } } public class Chore { private String name; private int step; private int id; public Chore(String name) { this.name = name; this.step = this.name.length(); this.id = 0; } public synchronized String digest() { String message = "" + id + name.charAt(id); try { Thread.sleep(500); } catch (InterruptedException ie) { } id++; return message; } public synchronized boolean doEnd() { return id >= step; } } public class Manager extends Thread { private String name; private Assistant a; public Manager(String name) { this.name = name; } public void run() { check(); } public void check() { a.restart(); } } public class Test { public static void main(String[] args) { Chore[] ch = { new Chore("掃除"), new Chore("プリント印刷"), new Chore("出欠データ入力") }; Assistant[] a = { new Assistant("あ", ch[0]), new Assistant("\tい", ch[1]), new Assistant("\t\tう", ch[2] }; Thread[] t = new Thread[a.length]; for (int i = 0; i < t.length; i++) { t[i] = new Thread(a[i]); } for (int i = 0; i < t.length; i++) { t[i].start(); } Manager m = new Manager("監査"); m.setDaemon(true); m.start(); for (int i = 0; i < t.length; i++) { try { t[i].join(); } catch( InterruptedException ie ) { } } }

    • ベストアンサー
    • Java
  • java

    次のコード中の括弧内で下に示す11通りの各コードを実行した場合の実行画面を正確に答えよ。 class A{ public void func1(){System.out.println("A1");} public void func2(){System.out.println("A2");} } class B extends A{ public void func1(){System.out.println("B");} } class C{ public int x=0, y=1; } class D extends C{ public int x=2; public void func1(int x){System.out.println(x);} public void func2(int x){System.out.println(this.x);} public void func3(int x){System.out.println(super.x);} public void func4(int x){System.out.println(this.y);} public void func5(int x){System.out.println(super.y);} } class E{ public void func1(int n){ try{ System.out.println("E1"); int[] ary=new int[n]; System.out.println("E2"); }catch(NegativeArraySizeException e){ System.out.println("E3"); }finally{ System.out.println("E4"); } } } (1) A a=new A(); a.func1(); (2) A a=new B(); a.func1(); (3) B b=new B(); b.func1(); (4) B b=new B(); b.func2(); (5) D d=new D(); d.func1(3); (6) D d=new D(); d.func2(3); (7) D d=new D(); d.func3(3); (8) D d=new D(); d.func4(3); (9) D d=new D(); d.func5(3); (10) E e=new E(); e.func1(5); (11) E e=new E(); e.func1(-2); (1) A1 (2)B (3)B (4) A2 (5) 3 (6) 2 (7) 0 (8) 1 (9) 1 (10) E1E2E4(11) E1E3E4 と答えになるんですがなぜこうなるのかわかりません。教えてください

    • ベストアンサー
    • Java
  • ThreadとRunnableのlogへの表示差異

    マルチスレッドでsleep()の時間を指定して動かすのを前提で、 Threadを継承した時は、時間通りに(リアルタイムで) eclipseのコンソールへ表示されるのですが、 Runnableをインプリメントしての動作では、 eclipseのコンソールに一瞬で表示されるのは何故でしょうか? public class MultiThread3 implements Runnable{ int time; MultiThread3(int time) { this.time = time; } public void run() { for(int i=0; i<5; i++) System.out.println("No."+i+":"+Thread.currentThread().getName()); try{ Thread.sleep(time); }catch (InterruptedException e) {} } } class MultiThreadTest3{ public static void main(String[] args) { MultiThread3 a = new MultiThread3(500); MultiThread3 b = new MultiThread3(700); MultiThread3 c = new MultiThread3(1100); Thread ta = new Thread(a); Thread tb = new Thread(b); Thread tc = new Thread(c); ta.setName("A"); tb.setName("\tB"); tc.setName("\t\tC"); ta.start(); tb.start(); tc.start(); } }

    • ベストアンサー
    • Java
  • javaのローカル変数について

    基本的なことの質問ですがよろしくお願いいたします。 下記の様なソースコードでint y ;はローカル変数にあたると思いますが 確かローカル変数は初期化されないとコンパイルエラーになるはずなのですが どうもコンパイルも実行もできます。 どうしてでしょうか? よろしくお願いいたします。 class Test1{ static int x = 0; } public class Test extends Test1{ public static void main(String args[]){ Test i = new Test(); int y ; y = ++x; System.out.println( y ) ; } }

    • ベストアンサー
    • Java
  • hashCode

    ObjectのhashCodeはどういう規則で生成されているのでしょうか? あるオブジェクトにおいて、オブジェクトの生成順番が実行毎に同じならばhashCodeは同一になるかと思っていたのですが、どうも違うようなので気になりました。 例えば、マルチスレッドでObjectの生成順序を毎回違うよう生成しても、ob配列ojbs1,objs2中の要素のhashCode()は同じになります。 public class HashCodeMultiThreadTest {  public static void main(String[] args) {   final Object[] objs1 = new Object[20];   final Object[] objs2 = new Object[20];   int one_or_two = (int) (Math.random()*2+1);   final long sleep_time1 = 100 * one_or_two;   final long sleep_time2 = 100 * (3-one_or_two);      Thread t1 = new Thread() {    public void run() { try{     for (int i=0; i<objs1.length; i++) {      objs1[i] = new Object();      Thread.sleep(sleep_time1);     }    }catch(Exception e){}}   };   Thread t2 = new Thread() {    public void run() { try {     for (int i=0; i<objs2.length; i++) {      objs2[i] = new Object();      Thread.sleep(sleep_time2);     }    }catch(Exception e){}}   };   try {    t1.start();    t2.start();    t1.join();    t2.join();   }catch(Exception e){}   for (Object o : objs2)    System.out.println(o.hashCode());  } }

    • ベストアンサー
    • Java
  • javaのスレッドの割込みについて

    javaのスレッド割込みに関する初心者の質問です。 割込みの発生元でCNTに値を設定し、割込みの受信元でCNTの値を読込み、表示しています。 下記のコードを参照ください。 Q1)CNTの値の設定と読込みで競合が発生する様なことはありませんか? 又は、処理が停滞するとか 以上、お手数をお掛けしますが、宜しくお願いします。 //割り込を発生するスレッド class threadA extends base{ private Thread target; public threadA(Thread targetA){ target = targetA; } public void run(){ for(int i = 0; i < 8;i++){ try{ System.out.print("."); Thread.sleep(1); CNT=i; target.interrupt(); }catch(InterruptedException e){ System.out.println("今割り込まれました: thread"); } } } } //スレッドからの割り込みを受信するルーチン class Sample108{ public static void main(String args[]){ //for(int i=0; i<2; i++){ classA obj = new classA(); obj.threadStart();//割り込みを受けるルーチン、この中で、割り込みを発生するルーチンを起動している //} } } //割り込みを受けるルーチン class classA extends base{ void threadStart(){ System.out.println("Start: main****"); threadA obj = new threadA(Thread.currentThread()); obj.start(); //Thread起動 //mainのルーチン for(int j = 0;j < 20; j++){ try{ Thread.sleep(1); System.out.print("*"); }catch(InterruptedException e){ System.out.println("今割り込まれました: classA CNT="+CNT); } } } } 以上

    • ベストアンサー
    • Java
  • 質問です。

    コンパイルはできるのですが、実行するとExption in thread"main"java.lang ArrayIndex0ut0fBoundsExcept ion :0 at java1_1.main(java1_1.java:19と表示をされてしまってできません。教えてください。宜しくお願いします。 /* 実行時引数を受け取り乱数を設定 */ class java1-1 { public static void array_syokika(int a[]) { for(int i = 0; i < a.length; i++) { a[i] = (int)(100 * Math.random()); } } public static void main(String args[]) { int num = Integer.parseInt(args[0]); int a[] = new int[num]; array_syokika(a); for(int i = 0; i < a.length; i++) { System.out.println("a[" + i + "]=" + a[i]); } } }

    • ベストアンサー
    • Java
  • JAVAのマルチスレッドの共有変数についての質問

    JAVAのマルチスレッドの共有変数についての質問です。 スレッドを2つ作ってその2つのスレッド共有の配列を作りたいのですがどうすれば良いのでしょうか。 コードは以下のとおりです。 public class testes extends Thread { int n; int a[]={10,10}; public testes(int n){ this.n = n; } public static void main(String[] args) { // TODO 自動生成されたメソッド・スタブ testes t1 = new testes(0); testes t2 = new testes(1); t1.start(); t2.start(); } public void run(){ a[n] =n; System.out.println(a[0]+","+a[1]); } } 実行すると(0,10)もしくは(10,1)が表示されます。 このとき(0,1)と表示するにはどうすれば良いのでしょうか。

専門家に質問してみよう