正しいスレッドプログラム

ここまでスレッドのサンプルを書いてきたのですが、主にプログラムがみにくくなるとか、めんどいとか、動くからえぇやんという理由で、やるべきことをやってないところがあります。
スレッドからの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);
    }
}