Javaでマルチスレッドデザインパターン(Balkingパターン編)

Balkingパターンとは

マルチスレッドのデザインパターンの1つで、以下の特徴を持つパターンです。

  • オブジェクトが特定の状態であれば、処理を実行
  • オブジェクトが特定の状態でなれれば、何も処理をしない

図示すると以下のようになります。
drawit-diagram-8
早速JavaでBalkingパターンを使って、以下仕様のプログラムを書いていきます。

サンプルプログラム仕様

動作仕様

  • 現在のデータの内容を定期的にファイルに書き込む
  • 前回の書き込みからデータの内容に違いがない場合は、書き込まない(上図のGuardに相当する条件)

登場クラス

  • Data (変更・保存ができるデータを表す)
  • SaverThread (データの内容を定期的に保存する)
  • ChangerThread (データの内容を変更・保存する)
  • Main

サンプルプログラム

Data

変更・保存ができるデータを表すクラスです。上図のGuardedOBjectに相当します。

public class Data {
    private final String filename;   // 保存するファイル名
    private String content;          // データの内容
    private boolean changed;         // 変更した内容が保存されてないならtrue

    public Data(String filename, String content){
        this.filename = filename;
        this.content = content;
        this.changed = false;
    }

    public synchronized void change(String newContent){
        content = newContent;
        changed = true;
    }

    public synchronized void save() throws IOException{
        if(!changed){
            return;
        }
        doSave();
        changed = false;
    }

    public void doSave() throws IOException {
        System.out.println(Thread.currentThread().getName() + " calls doSave, contend = " + content);
        Writer writer = new FileWriter(filename);
        writer.write(content);
        writer.close();
    }
}

このクラスのポイントは、以下の2点です。

  • chage/saveメソッドがsynchronizedにより排他されている
  • saveメソッドでchanged=falseの場合はreturnしている(つまりbalkしている)

SaverThread

データの内容を定期的に保存するクラスです。1秒おきに保存しています。

public class SaverThread extends Thread {
    private final Data data;

    public SaverThread(String name, Data data) {
        super(name);
        this.data = data;
    }

    public void run(){
        try {
            while (true) {
                data.save();
                Thread.sleep(1000);
            }
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

このクラスのポイントは、以下の点です。

  • SaverThreadクラスはThread
  • Dataクラスのsaveメソッドにより保存している

ChangerThread

データの内容を変更・保存するクラスです。

public class ChangerThread  extends Thread {
    private final Data data;
    private final Random random = new Random();

    public ChangerThread(String name, Data data){
        super(name);
        this.data = data;
    }

    public void run(){
        try {
            for (int i = 0; true; i++) {
                data.change("No." + i);
                Thread.sleep(random.nextInt(1000));
                data.save();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

このクラスのポイントは、以下の点です。

  • ChangerThreadクラスはThread
  • Dataクラスのchangeメソッドにより変更している
  • Dataクラスのsaveメソッドにより保存している

サンプルプログラムの実行

Main

こちらのMainクラスから上記3クラスを呼び出してみます。

public class Main {
    public static void main(String[] args) {
        Data data = new Data("data.txt", "(empty)");
        new ChangerThread("ChangerThread", data).start();
        new SaverThread("SaverThread", data).start();
    }
}

このMainのポイントは、以下の点です。

  • DataクラスをChangerThreadとSaverThreadで共有している

実行結果は以下のようになりました。

SaverThread calls doSave, contend = No.0
SaverThread calls doSave, content = No.1
SaverThread calls doSave, content = No.2
ChangerThread calls doSave, content = No.3
SaverThread calls doSave, content = No.4
ChangerThread calls doSave, content = No.5
SaverThread calls doSave, content = No.6
SaverThread calls doSave, content = No.7
ChangerThread calls doSave, content = No.8
ChangerThread calls doSave, content = No.9
SaverThread calls doSave, contend = No.10

こちらのポイントは、以下の点です。

  • SaverThread/ChangerThreaでdoSaveがランダムに実行されているが
  • contentは重複・欠落なくインクリメントされる

Dataクラスのsynchronized内でのみ変更されていたフィールドchangedがGuard条件となり、contentに変更がない場合は、保存処理は実行されないため、contentが重複なくインクリメントされます。

以上、Balkingパターンのサンプルでした。

参考

本記事の内容は、以下の書籍から引用して書かせて頂きました。(いつもお世話になっています)
増補改訂版 Java言語で学ぶデザインパターン入門 マルチスレッド編

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

CAPTCHA