ここまでスレッドのサンプルを書いてきたのですが、主にプログラムがみにくくなるとか、めんどいとか、動くからえぇやんという理由で、やるべきことをやってないところがあります。
スレッドからのSwing操作とwaitの処理です。
Swingはシングルスレッドモデルで実装されているので、GUIスレッドとは別のスレッドからSwingを操作するべきではありません。EventQueue#invokeLaterなどを使って処理をGUIスレッドにのせる必要があります。(SwingUtilities#invokeLaterは内部でEventQueue#invokeLaterを呼び出しているだけです)
今回の一連のサンプルで使っているJTextField#setTextや、今回は使ってないけどよく使うJTextArea#appendなど、JTextComponentのテキスト操作に関しては、幸いスレッドセーフなのでそのまま使えますが、JFrame#setVisibleなどほとんどのSwingメソッドはスレッドセーフではありません。
mainメソッドからはじまるスレッドも、GUIスレッドとは違うので、今回のサンプルでは処理全体をinvokeLaterにしておくべきです。
たとえば処理全体をhogeメソッドに入れて
private static void hoge(){ //処理 }
このメソッドをinvokeLaterから呼び出します
EventQueue.invokeLater(new Runnable(){ @Override public void run(){ hoge(); } }
もうひとつ、waitの問題。
waitがnotifyがなくても再開することがあるという問題で、JavaDocではObject#waitでは「スプリアスウェイクアップ」、Condition#awaitでは「見せかけの起動」とかかれてます。
そのため、Object#waitやCondition#awaitは、再開条件でのループで囲む必要があります。
これはEffective Javaで指摘されたことにより有名になりました。そのため、JDK1.4までのJavaDocには記述がありません。
http://sdc.sun.co.jp/java/docs/j2se/1.4/ja/docs/ja/api/java/lang/Object.html#wait()
http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/Object.html#wait()
ということで、LockConditionSampleでこの問題に対応します。
とりあえずフラグを用意します。ここでは匿名クラスで使うためにfinalをつけるので、配列にしてます。
//ロック用オブジェクト final Lock lock = new ReentrantLock(); final Condition condition = lock.newCondition(); final boolean[] flag = {false};
awaitメソッドの呼び出しをwhileで囲みます。
flag[0] = false; while(!flag[0]){ condition.await(); }
で、signalメソッド呼び出し時にフラグを立てます。
try{ lock.lock(); flag[0] = true; condition.signal(); }finally{ lock.unlock(); }
というか、これは各サンプルでちゃんとやっておこう。
全体のソース
import java.awt.EventQueue; import java.awt.FlowLayout; import java.awt.event.*; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.swing.*; public class LockConditionSample { public static void main(String[] args){ EventQueue.invokeLater(new Runnable() { @Override public void run() { hoge(); } }); } private static void hoge(){ JFrame f = new JFrame("Lockサンプル"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setLayout(new FlowLayout()); final JTextField tf = new JTextField(); tf.setColumns(12); f.add(tf); //ロック用オブジェクト final Lock lock = new ReentrantLock(); final Condition condition = lock.newCondition(); final boolean[] flag = {false}; //スレッド final Thread t = new Thread(){ @Override public void run() { try { lock.lock(); for(int i = 3; i > 0; --i){ tf.setText("カウント" + i); Thread.sleep(1000); } tf.setText("待機中"); flag[0] = false; while(!flag[0]){ condition.await(); } tf.setText("きた!"); } catch (InterruptedException ex) { tf.setText("中断"); }finally{ lock.unlock(); } } }; t.start(); JButton b; //再開ボタン b = new JButton("再開"); f.add(b); b.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //ボタン押されたらnotify try{ lock.lock(); flag[0] = true; condition.signal(); }finally{ lock.unlock(); } } }); //中断 b = new JButton("中断"); f.add(b); b.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { //ボタン押されたらinterrupt t.interrupt(); } }); f.pack(); f.setVisible(true); } }