みんなの「教えて(疑問・質問)」にみんなで「答える」Q&Aコミュニティ

こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

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 ) {
}
}
}

投稿日時 - 2013-07-15 15:09:07

QNo.8177341

すぐに回答ほしいです

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

自環境(Windows7、32bit)で発現しないため、詰まっています。
waitかnotify... 

ManagerクラスのcheckメソッドのExceptionをキャッチする段にスタックトレースを仕掛けて、
}catch (Exception e){
e.printStackTrace();
}
何行目かって分かりますか?

貴実行環境も参考までに教えていただきたいです。

あるいは、
>javac -g Test0715.java でコンパイルし、
jdbで例外発生時のスレッドの実行状況を把握するために以下のコマンドを投入してスタックトレースを見たいです。
>jdb Test0715
>catch IllegalThreadStateException
>run で実行
IllegalThreadStateExceptionをキャッチしたら
>suspend
>where all ←このコマンドの出力結果をいただきたいです。

投稿日時 - 2013-07-16 13:29:23

お礼

提出期限が過ぎてしまったので、
締切とさせていただきます。
今までの回答ありがとうございました。

投稿日時 - 2013-07-16 14:00:57

ANo.9

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(9)

ANo.8

現状のソースがどうなっているのかを拝見したいです(エラーが出るソース)。

投稿日時 - 2013-07-16 10:56:21

補足

あなたがくださったソースを実行しました。
コンパイルはとおりますが、実行途中で先ほど言った症状が出ます

投稿日時 - 2013-07-16 12:11:00

ANo.7

waitはloafのなかでしています。

Managerのcheckが絶えずチェックし続けるため、waitしていないように見えます。

Managerのcheckメソッドを以下のように編集して、sleepを追加すると、チェック間隔が広くなり、Assistantのwaitも把握できると思います。
public void check() {
try{
while(!stop){
for (int i = 0; i < t.length ; i++){
System.out.println(i + " - " + t[i].getState());
if(t[i].getState() == Thread.State.WAITING)
a[i].restart();
}
try { Thread.sleep(1000); }
catch (InterruptedException ie) { }
}
}catch (Exception e){
System.out.println(e.toString());

}
}

これでも違和感があれば、求める実行結果を詳しく教えていただきたいです。

投稿日時 - 2013-07-16 06:22:57

補足

実行してみたのですが、
一番目のスレッドがwaitされてnotifyされたとこまでは確認出来ましたが、
そのあと、IllegalThreadStateExceptionが発生し、エラーの嵐になります

投稿日時 - 2013-07-16 07:32:55

ANo.6

http://kie.nu/197g パスワード:oshiete

現作業ソースです。
- 1ファイルに抑えるため、パブリッククラスはmain以外ただのclassにしています。
- 当方のファイル管理上Test0715をmainメソッドのクラスとしています。

(実行結果)以下を出力して終了…これが正しいかもわかっていませんが。
う : 0出
い : 0プ
あ : 0掃
う : 1欠
い : 1リ
あ : 1除
い : 2ン
う : 2デ
い : 3ト
う : 3ー
い : 4印
う : 4タ
い : 5刷
う : 5入
う : 6力

投稿日時 - 2013-07-15 22:17:44

補足

これいつwaitしてるんどすか?

投稿日時 - 2013-07-16 02:36:43

ANo.5

では、AssitantクラスのnotifyAllをnotify()に変えて、

public synchronized void restart() {
notify();
}

Managerクラスを以下のようにしてみます。

class Manager extends Thread {
private String name;
private Thread[] t;
private Assistant[] a;

public boolean stop = false;

class Manager extends Thread {
public Manager(String name, Thread[] t, Assistant[] a) { this.name = name; this.t = t; this.a = a;}

public void run() { check(); }

public void check() {
try{
while(!stop){
for (int i = 0; i < t.length ; i++){
if(t[i].getState() == Thread.State.WAITING)
a[i].restart();
}
}
}catch (Exception e){
System.out.println(e.toString());

}
}
}

これで前回答のisWaitは不要です。本来はスレッドの状態を見たかったのでこうすべきでしょう。

投稿日時 - 2013-07-15 19:59:04

補足

これだと最初に実行されたスレッドにしか適応されず、止まったままになってしまいます。
ManagerのstartをAssistantのスレッドのループ終了後の実行していますがうまくいきません

投稿日時 - 2013-07-15 20:35:30

ANo.4

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() {
isWait = true;
try { wait(); }
catch (InterruptedException e) { e.printStackTrace(); }
isWait = false;
}

public synchronized void restart() {
if (isWait) { notifyAll(); }
}
}
class Manager extends Thread {
private String name;
private Assistant[] a;

public boolean stop = false;

public Manager(String name, Assistant[] a) { this.name = name; this.a = a;}

public void run() { check(); }

public void check() {
try{
while(!stop){
for (int i = 0; i < a.length ; i++){
a[i].restart();
}
}
}catch (Exception e){
System.out.println(e.toString()); //例外が発生して、こちらに落ちた→ループ抜けてスレッドも死んだ

}
}

}

Testクラス:
変更1
Managerクラスの生成は、Manager m = new Manager("監査", a);として、Assistantクラスを渡しています。
変更2
joinのループを抜けた後に以下を追加
m.stop = true;


(1)Managerクラスにチェックし続ける処理(と抜ける条件)を追加
(2)waitをスレッドを眠らせる
(3)起こすときは一度に全員起こす

なぜChoreのインスタンスにwaitを掛けていたのかが謎だったので、スレッドを寝かすようにさせて頂きました。意図がありますか?

投稿日時 - 2013-07-15 17:50:21

補足

いえ、私のミスです。
すみません。
1つ訂正なのですが、Managerクラスがスレッドを順番に見ていき、眠っていたら起こすようにするなので、全員起こすではないです。
notifyAllは使わないです。

投稿日時 - 2013-07-15 17:58:35

ANo.3

>checkメソッドを設け、Assistantをcheckし続ける
これをやってないです。
クラスTest内のAssistantインスタンスとManagerインスタンス間に、
何の関連も作られてないので、Managerのcheckメソッドが機能していません。

投稿日時 - 2013-07-15 16:22:00

補足

Assistantの配列をManagerに渡してやってもみたのですが、
どうもうまくいきません。
まず、Managerのstartメソッドをループさせ続けなければいけませんし

投稿日時 - 2013-07-15 16:29:04

ANo.2

私の前回答は破棄してください。申し訳ありません。

アシスタントスレッドを起こすタイミング(notify)が一度しかありませんでした。この時はIllegalMonitorStateExceptionが発生しています。
そしてそのタイミングは、アシスタントスレッドを起こした直後にで、その後発生せず、main内のjoinに入るため、Manager - mainも待ちに入ります。

こうして全てのスレッドが待ち状態になり、プロセスそのものがストップした状態になったプログラムとなってしまっています。

解決策は考えてみますが、設計し直しも必要かもしれません。

投稿日時 - 2013-07-15 16:10:00

補足

クラス設計は変えずになので、
メソッドの中身を変えるしか方法はないです。

投稿日時 - 2013-07-15 16:19:49

ANo.1

public void check() { a.restart(); }
ここだけa.restartなのが気になります。他はt[i].methodnameなので。

投稿日時 - 2013-07-15 15:34:54

補足

そのへんはとりあえずAssistantクラスをManagerクラスが持っておいて、Assistantクラスのrestartメソッドを呼び出すためにやっています。

今は一旦Manager関連はいったんコメントアウトして、Assistantクラスのworkメソッドで、loafを呼び出した後にrestartメソッドを呼び出しているのですが、呼ばれなく詰んでいるところです。

投稿日時 - 2013-07-15 15:39:54

あなたにオススメの質問