wait() notify() ではまった。
自宅サーバも何とか組めたのでプログラミングでもやるべと、Javaの思い出し作業をしております。そんでもって、まぁお勉強と言っても教科書の練習問題をちびちび書いてたのですが、Thread 制御のところで思わずはまってしまったのでご紹介。
大体次のようなことをしたいプログラムを書いてたのですがなぜかマルチThreadで動いてくれなくて困ったというお話です。
Resouceを使うと言っても自身のnameを標準出力するとかそういうものです。
しかしてあとあと楽しいこともできるかもしれません。
さてこれを実現するために(またいい加減な図を出しますが)次のようなクラスを作りました。
Recouce, User, Controllerの3つのクラスを作ります。
Controllerは何もしない子で、main関数があると思って下さい。ControllerでUserを複数とResourceを1個作り、各Userに同じResourceのインスタンスを渡します。UserクラスはThreadクラスを継承しており、ResouceにアクセスするメソッドuseResource()を複数インスタンスが各Thread上で実行します。useResource()はsyncronizedで排他制御してあります。
ResourceはusedFlagというステータスを持たせてあります。
usedFlag=falseの時にしかアクセスできないようにします。
useResouce() の最初の処理で while ループを使い usedFlag=true ならば wait() で待たせておきます。処理の終わったThread が usedFlag=true, notifyAll() することで、次のThread が動き出します。
(ソースおけよって話ですが)
で、めでたく動き出すはずだったのですが。
なぜか動くのは同じThread(便宜上Thread01)のみ。他のThreadはnotifyAll() しても動き出さない。Threadを1000個とか作ってみてもThread01しか動かない。
なにこれ。
四苦八苦いろいろ調べているうちに、多分これだろうというのを見つけました。
syncronized メソッドの有効範囲です。syncronized メソッドはそのメソッドが定義されているクラスのインスタンスに対するアクセスをロックするようです。(インスタンスメソッドの場合。クラスメソッドの場合はThisをロックします。)
[ 詳しくはこちら、Javaの道!http://www.javaroad.jp/java_thread4.htm ]
今回の場合、useResource() がロックしてたのは、Userのインスタンスっぽいです。
なので、各Userインスタンスの中でwait() して、各々のUserインスタンスの中で notify() しようとしてたようです。そら、一つのインスタンスだけが wait() と notify() を繰り返しますね。他のThread には notifyAll() が伝わってなかったようです。
そこで改修。
Resource を使いますよというメソッドをResourceに持たせてしまいます。
これでアクセスされるのをロックするのはResourceインスタンスになります。
(あくまでアクセスしにいくのはUserのThreadです。)
さて、めでたくコードも動きました。Thread難しいですね。難しくないですか。。
ちなみに上のクラス設計はちょっとしたアハ体験で、
「AがBにほげほげする」
というほげほげ処理を、AではなくBに持たせようということなんですね。
なるたけ使われる側(Resource)に処理を押し付けてしまおうなんて、どっかの教科書に書いたあったような。。オブジェクト思考!
なのかなとか思ったりしました。
ほんとは処理としてクラス分けするのかもしれないけれど。
奥深いですね。