JavaのObject.waitメソッドの使い方【初心者向け】

初心者向けにJavaのObject.waitメソッドの使い方について解説しています。最初にマルチスレッドプログラミングについて学習します。次にwaitメソッドを使って処理を一時停止させる書き方を覚えましょう。

TechAcademyマガジンはオンラインのプログラミングスクールTechAcademy [テックアカデミー]が運営する教育×テクノロジーのWebメディアです。初心者でもすぐ勉強できる記事が2,000以上あります。

JavaのObject.waitメソッドの使い方について解説します。

実際にプログラムを書いて説明しているので、ぜひ理解しておきましょう。

 

なお本記事は、TechAcademyのJava講座の内容をもとに作成しています。

 

田島悠介

今回は、Javaに関する内容だね!

大石ゆかり

どういう内容でしょうか?

田島悠介

Object.waitメソッドの使い方について詳しく説明していくね!

大石ゆかり

お願いします!

 

Object.waitメソッド(Objectクラスのwaitメソッド)とは

waitメソッドの解説の前に、まずマルチスレッドのプログラムについて説明します。

プログラム内のひとつの処理の流れのことをスレッドという単位で数えます。

通常のプログラムは一度にひとつづつ処理を行い、これをシングルスレッドの処理と言います。
対して、同時に複数の処理を並行して行うことをマルチスレッドの処理と言います。

このマルチスレッド処理の作成時に、あるスレッドの処理を他のスレッドの処理の終了後に行いたいとします。

そういったときにwaitメソッドを使用します。

waitメソッドはこのメソッドを呼び出したスレッドの処理を一時停止させます。

そして、この停止したスレッドを再開するにはnotify、または、notifyAllというメソッドを使用します。

このメソッドを呼ぶと停止しているスレッドの処理を再開させることができます。

 

Object.waitメソッドの使い方

waitメソッドを使用する際は以下の様にInterruptedExceptionについて例外処理する必要が有ります。

try {
  wait();
} catch (InterruptedException e) {
  //例外処理
}

waitメソッドは下記の様にsynchronizedで囲われた範囲でないと使用できません。
(例1:synchronizedで修飾したメソッド内で使用する場合)

synchronized void testWait() {
  try {
    wait();
  } catch (InterruptedException e) {
    //例外処理
  }
}

(例2:synchronizedブロック内で使用する場合)

void testWait(){

  synchronized (this) {
    try {
      wait();
    } catch (InterruptedException e) {
      //例外処理
    }
  }
}

これは処理再開時にnotifyメソッド、notifyAllメソッドを使用する際も同様です。
(例1:synchronizedで修飾したメソッド内で使用する場合)

synchronized void testNotify() {
  notify();
}

synchronizedという修飾子はインスタンスにロックをかけるために使用します。

ロックをかけるというのは、あるインスタンスに対して同時に1つのスレッドしか参照できない様にすることです。

wait、notify、notifyAllのメソッドは使用する際にそのインスタンスのロックをかける必要が有るため上記の様にsynchronizedで囲われた範囲で使用しないといけません。

notifyメソッド、notifyAllメソッドの違いは下記の通りです。

notifyメソッド

停止しているスレッドの内、どれか一つの処理を再開します。

停止しているスレッドが複数ある場合、どれが再開するかは決まっていません。

 

notifyAllメソッド

停止しているスレッドを全て再開します。

 

最後にThreadクラスとRunableインターフェースについて、簡単に説明します。

マルチスレッドで動作させたいクラスは、上記のクラスを継承または実装する必要が有ります。

どちらも同じことが可能ですが、Runableはインターフェースなので、同時に他のクラスを継承することもできるというメリットが有ります。

 

[PR] Javaで挫折しない学習方法を動画で公開中

実際に書いてみよう

下記のプログラムは左欄で説明した機能を実際に動かすプログラムです。

スレッドの起動→停止→再開を行っています。

このプログラムではwait( )に引数で数値を渡していますが、これは停止したスレッドが待つ時間の長さをミリ秒で指定しています。

こうすることでnotifyで再開されなかったスレッドも一定時間後に自動的に再開する様になります。

WaitTarget.wtNotify( )で呼ぶメソッドをnotifyからnotifyAllに変えてみて動作が変わることも確認してみてください。

package wait_sample;
public class WaitTarget{

  public synchronized void count(String str) throws InterruptedException{
    System.out.print(str+"の処理を開始:");
      for (int i = 3; i > 0; i--) {
        System.out.print(i+"→");
        Thread.sleep(500);//0.5秒待つ
      }
      System.out.println("0");
  }

  public synchronized void wtWait(String str) throws InterruptedException {
      System.out.println(str+"の処理を停止");
      wait(10000);//最大で10秒間停止する
  }

  public synchronized void wtNotify() {
      System.out.println("停止中の処理を再開する");
    notify();//停止中のスレッドを再開
  }
}
package wait_sample;
public class WaitThread implements Runnable{

  String str;
  WaitTarget wt;

  public WaitThread(String str, WaitTarget wt){
    this.str = str;
    this.wt = wt;
  }

  @Override
  public void run(){
    try {
      wt.count(str);
      wt.wtWait(str);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(str+"を終了");
  }
}
package wait_sample;
public class NotifyThread extends Thread{

  WaitTarget wt;

  public NotifyThread(WaitTarget wt){
    this.wt = wt;
  }

  @Override
  public void run() {
    wt.wtNotify();
  }
}
package wait_sample;
public class WaitSampleMain {

  public static void main(String[] args) {

    //スレッド処理の対象となるオブジェクトを生成
    WaitTarget wt = new WaitTarget();

    try {
      //A〜Dのスレッドを開始
      WaitThread ws1 = new WaitThread("スレッドA",wt);
      WaitThread ws2 = new WaitThread("スレッドB",wt);
      WaitThread ws3 = new WaitThread("スレッドC",wt);
      WaitThread ws4 = new WaitThread("スレッドD",wt);
      Thread t1 = new Thread(ws1);
      Thread t2 = new Thread(ws2);
      Thread t3 = new Thread(ws3);
      Thread t4 = new Thread(ws4);
      t1.start();
      t2.start();
      t3.start();
      t4.start();

      Thread.sleep(8000);//8秒待つ

      NotifyThread ns = new NotifyThread(wt);
      ns.start();

    } catch (InterruptedException e) {
      e.printStackTrace();
    }catch (Exception e) {
      e.printStackTrace();
    }
  }
}

 

実行結果の例

スレッドAの処理を開始:3→2→1→0
スレッドAの処理を停止
スレッドDの処理を開始:3→2→1→0
スレッドDの処理を停止
スレッドCの処理を開始:3→2→1→0
スレッドCの処理を停止
スレッドBの処理を開始:3→2→1→0
スレッドBの処理を停止
停止中の処理を再開する
スレッドAを終了
スレッドDを終了
スレッドCを終了
スレッドBを終了

 

監修してくれたメンター

堀田 悠貴

以前はSEとして某大学病院の電子カルテシステムの保守・開発に携わっていました。
基本的にJavaを使用してクライアント側のプログラム開発を担当していました。

基本業務はJavaでしたが案件次第で色々他のことにも手を出す必要があり、その都度苦労した記憶があります。
現在はその会社を退社して、こちらでのメンター業務のみ行っています。

 

大石ゆかり

内容分かりやすくて良かったです!

田島悠介

ゆかりちゃんも分からないことがあったら質問してね!

大石ゆかり

分かりました。ありがとうございます!

また、オンラインのプログラミングスクールTechAcademyでは、Java講座を開催しています。

JavaやServletの技術を使ってWebアプリケーションの開発を学ぶことができます。

現役エンジニアがパーソナルメンターとして受講生に1人ずつつき、マンツーマンのメンタリングで学習をサポートし、最短4週間で習得することが可能です。

また、現役エンジニアから学べる無料のプログラミング体験会も実施しているので、ぜひ参加してみてください。