質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.36%

コンストラクタに書いたJButtonでnotifyする方法

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,056

vicreo32

score 14

前提・実現したいこと

課題で簡単なStopwatchを作っています。
JButtonで作ったスタートボタンを押すと、waitになっていたオブジェクトをnotifyするように作りたいです。
デッドロックが原因だと予測しているのですが、その回避の方法がわかりません。
また、今回質問している箇所の他にもおかしな箇所があれば教えてほしいです。

発生している問題・エラーメッセージ

ボタン自体は動作しているのですが、notifyが動作しません。
おそらく、waitとnotifyどちらもsynchronizedにしているので、デッドロックになってしまっていると予測しています。

該当のソースコード

public class stopwatch2 extends JPanel{
    static JLabel label;
    JButton b1,b2,b3;
    public static final int FPS = 0;
    private static long startTime;
    private static boolean isRunning = false;
    private static long time;
    public stopwatch2(){

        label =new JLabel(String.valueOf(time));
        b1 = new JButton("Start");
        b1.addActionListener(
                  new ActionListener(){
                    public synchronized void actionPerformed(ActionEvent event){
                        isRunning = true;
                        notify();
                    }
                  });
        b2 = new JButton("Stop");
        b2.addActionListener(
                  new ActionListener(){
                    public void actionPerformed(ActionEvent event){
                      isRunning = false;
                    }
                  });
        b3 = new JButton("Reset");
        setLayout(new BorderLayout());
        add(label,BorderLayout.NORTH);
        add(b1,BorderLayout.CENTER);
        add(b2,BorderLayout.EAST);
        add(b3,BorderLayout.SOUTH);
    }

    public static void main(String[] args) throws InterruptedException {
        // TODO Auto-generated method stub
        JFrame frame = new JFrame("Stopwatch");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        stopwatch2 sw = new stopwatch2();
        frame.add(sw);
        frame.pack();
        frame.setVisible(true);
        System.out.print(isRunning);
        while(true){
            System.out.print(Thread.currentThread());
            synchronized(sw){
                sw.wait();   //swがついてるメソッドの間はsynchronizedされる
            }
            sw.startTime = System.currentTimeMillis();//ミリ単位の現在の時間をstartTimeに代入
            while(sw.isRunning){  //isRunningがtrueの間ループ
                //ラベルにtimeに入っているものを入れて更新し続ける
                //stopを押すと、isRunningにfalseを代入してループを止める。
                sw.label.setText(String.valueOf(System.currentTimeMillis()-startTime));
                sw.repaint();
                Thread.sleep(1000 / stopwatch2.FPS);
            }
        }

    }

}

ツール

eclipseを使っています。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

synchronizedの対象オブジェクト(インスタンス)が異なっているのが原因のように思えます。

mainメソッドでsynchronized(sw)としていますが、この際の同期対象オブジェクトはstopwatch2クラスのインスタンスです。それと排他制御をしようとしていると思われる箇所はstopwatch2のコンストラクター内での以下のコードだと思います。しかし、こう書くと同期対象インスタンスはActionListenerの無名クラスのインスタンスとなります。それぞれ違うインスタンスに対してsynchronizedしているので同期になっていないと思います。

b1.addActionListener(
  new ActionListener(){
    public synchronized void actionPerformed(ActionEvent event){
      isRunning = true;
      notify();
    }
  });

とりあえずの対処として、同一のインスタンスに対してsynchoronizedを用いるようにしてみてはどうでしょう。(それで期待通り動くかどうかまでは確認していません)

b1.addActionListener(
  new ActionListener(){
    public void actionPerformed(ActionEvent event){
      Object lock = stopwatch2.this; //このオブジェクトを同期に使う
      synchoronized (lock) {
        isRunning = true;
        lock.notify();
      }
    }
  });

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/07/03 23:53

    回答ありがとうございます。
    早速試してみたところ、startボタンを押すと、java.lang.IllegalMonitorStateExceptionというエラーが出ました。
    解決策をご存知でしたら教えていただけるとありがたいです。

    キャンセル

  • 2017/07/03 23:56

    失礼しました。notifyの方もなおさなければなりませんでしたね。回答を修正します。

    キャンセル

  • 2017/07/04 08:51

    無事にnotifyできました!
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.36%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る