android スレッドセーフはじめの一歩(2)

synchronizedメソッド

2つ以上の変数を操作するとき、それぞれの変数への操作をアトミックな操作にしていても、全体としてはスレッドセーフではありません。

public class Hoge {
    private AtomicInteger min = new AtomicInteger(0);
    private AtomicInteger max = new AtomicInteger(0);

    public void set(int min, int max) {
        this.min.set(min);
        this.max.set(max);
    }

    public int range() {
        return this.max.get() - this.min.get();
    }
}

スレッドセーフに改善:
メソッドに synchronizedキーワードをつけます。

synchronizedキーワードをつけると、そのメソッドを同時実行するスレッドはただ一つだけに制限されます。

複数のメソッドにsynchronizedキーワードをつけると、スレッドAがメソッドXを実行中に、スレッドBはメソッドYの実行を待たされます。

この例の場合は、setメソッドだけでなく、rangeメソッドにもsynchronizedキーワードをつけます。

public class Hoge {
    private AtomicInteger min = new AtomicInteger(0);
    private AtomicInteger max = new AtomicInteger(0);

    public synchronized void set(int min, int max) {
        this.min.set(min);
        this.max.set(max);
    }

    public synchronized int range() {
        return this.max.get() - this.min.get();
    }
}

synchronizedブロック

オブジェクトのメンバー変数を他のオブジェクトと共有している場合、synchronizedメソッドは効果がありません。

synchronizedメソッドは、オブジェクト自身へのスレッド同期化をしますが、複数オブジェクト間のスレッドの同期化は調整しないからです。

次の例では、複数のHogeオブジェクトで同じminとmaxを共有していると、スレッドセーフではありません。

public class Hoge {
    private AtomicInteger min;
    private AtomicInteger max;

    public Hoge(AtomicInteger min, AtomicInteger max) {
        this.min = min;
        this.max = max;
    }

    public synchronized void set(int min, int max) {
        this.min.set(min);
        this.max.set(max);
    }

    public synchronized int range() {
        return this.max.get() - this.min.get();
    }
}

スレッドセーフに改善:
synchronizedブロックで囲みます。

この例では、複数スレッド間で、minとmaxへの保存と参照を1つのスレッドに制限したいので、synchronizedブロックをネストして使います。

public class Hoge {
    private AtomicInteger min;
    private AtomicInteger max;

    public Hoge(AtomicInteger min, AtomicInteger max) {
        this.min = min;
        this.max = max;
    }

    public void set(int min, int max) {
        synchronized (this.min) {
            synchronized (this.max) {
                this.min.set(min);
                this.max.set(max);
            }
        }
    }

    public int range() {
        synchronized (this.min) {
            synchronized (this.max) {
                return this.max.get() - this.min.get();
            }
        }
    }
}
タイトルとURLをコピーしました