Java同時プログラミング-拡張スレッドセキュリティクラスについて考える


再利用は、開発とテスト(テストが自分で厳密に測定するよりも多い)を行う時間とその他のコストを節約します.
しかし,1つのスレッドセキュリティクラスを拡張する際には,いくつかの問題を考える必要がある.
たとえば、すべての公開メソッドにsynchronized修飾を提供し、アクセス反発と可視性を保証するスレッドセキュリティクラスVectorについてよく知られています.
しかし、Vectorは公有的な構造であり、顧客コードの不変性制約について何も知らない.
たとえば、クライアント・コードでVectorオブジェクトに対して2回連続してメソッドが呼び出されます.毎回スレッドが安全ですが、この複合操作は原子操作ではありません.これは私たちの不変性制約を満たさない可能性があります.スレッド・セキュリティ・クラスは「安全ではありません」になります.
データ構造クラスではput-if-absent操作をよく行います.
もちろん、スタックが閉じている場合や単一スレッドアプリケーションの場合は問題ありません.
しかし、マルチスレッドが同じデータ構造オブジェクトにアクセスする場合、この操作が安全かどうかを考慮する必要があります.
いわゆるスレッドセキュリティクラスを使用していても.
このスレッド・セキュリティ・クラスを再利用し、拡張し、不変性制約が破壊されないことを保証します.
public class BetterVector <E> extends Vector<E> {

    static final long serialVersionUID = -3963416950630760754L;



    public synchronized boolean putIfAbsent(E x) {

        boolean absent = !contains(x);

        if (absent)

            add(x);

        return absent;

    }

}


JDKの中のVectorを拡張しているので、安心感を与えるかもしれません(一部の仕様で定義されている同期戦略を除いては、誰も保証できません).
しかし、あなたの同僚が提供したスレッドセキュリティクラスなら?次のバージョンで変更されないことは誰も保証できません.
残念なことに、次のバージョンで変化するのは同期ポリシーであり、サブクラスが直接影響を受けることになります.
継承しない場合、既存のスレッドセキュリティクラスを再利用した場合に、独自の不変性制約を保証する方法を考える必要があります.
そこで必要な方法だけを考えて、継承が危険である以上、スレッドセキュリティオブジェクトをfieldとして、必要な機能を持ってきて使えばいいと思います.
しかし、次はエラーの例です.
class BadListHelper <E> {

    public List<E> list = Collections.synchronizedList(new ArrayList<E>());



    public synchronized boolean putIfAbsent(E x) {

        boolean absent = !list.contains(x);

        if (absent)

            list.add(x);

        return absent;

    }

}


注意が足りなければ、最初の反応は「大丈夫かな...」
確かに、私たちが使っているリストはCollectionsを使っています.ysnchronizedListは装飾されています.まして私たちが提供したputIfAbsentにもsynchronizedキーワードが付いています.この方法は確かに同期しています.
しかし、あるスレッドがputIfAbsentを呼び出すと、別のスレッドが他のメソッドを呼び出すこともできます.
このように私たちの不変性制約は破壊され、このhelperクラスは意味がありません.
つまり問題はこのsynchronizedであり、synchronized(this)ではなくsynchronized(list)が必要です.
だからhelperは次のように変更する必要があります.
class GoodListHelper <E> {

    public List<E> list = Collections.synchronizedList(new ArrayList<E>());



    public boolean putIfAbsent(E x) {

        synchronized (list) {

            boolean absent = !list.contains(x);

            if (absent)

                list.add(x);

            return absent;

        }

    }

}


ここまではいいですが、派生クラスとベースクラスにはいくつかの結合があります.
リストをprivate finalと宣言し、コンストラクタでCollectionsを使用するコンストラクタを提供することができます.ysnchronizedListで飾ります.
しかし,それでも挙動上の結合が存在し,ベースクラスの挙動にレンガを添えることはできない.
この問題を解決するには、組み合わせを使用します.
public class ImprovedList<T> implements List<T> {

    private final List<T> list;



    public ImprovedList(List<T> list) { this.list = list; }



    public synchronized boolean putIfAbsent(T x) {

        boolean contains = list.contains(x);

        if (contains)

            list.add(x);

        return !contains;

    }



    public int size() {

        return list.size();

    }



    public boolean isEmpty() {

        return list.isEmpty();

    }



    public boolean contains(Object o) {

        return list.contains(o);

    }



    public Iterator<T> iterator() {

        return list.iterator();

    }



    public Object[] toArray() {

        return list.toArray();

    }



    public <T> T[] toArray(T[] a) {

        return list.toArray(a);

    }



    public synchronized boolean add(T e) {

        return list.add(e);

    }



    public synchronized boolean remove(Object o) {

        return list.remove(o);

    }



    public boolean containsAll(Collection<?> c) {

        return list.containsAll(c);

    }



    public synchronized boolean addAll(Collection<? extends T> c) {

        return list.addAll(c);

    }



    public synchronized boolean addAll(int index, Collection<? extends T> c) {

        return list.addAll(index, c);

    }



    public synchronized boolean removeAll(Collection<?> c) {

        return list.removeAll(c);

    }



    public synchronized boolean retainAll(Collection<?> c) {

        return list.retainAll(c);

    }



    public boolean equals(Object o) {

        return list.equals(o);

    }



    public int hashCode() {

        return list.hashCode();

    }



    public T get(int index) {

        return list.get(index);

    }



    public T set(int index, T element) {

        return list.set(index, element);

    }



    public void add(int index, T element) {

        list.add(index, element);

    }



    public T remove(int index) {

        return list.remove(index);

    }



    public int indexOf(Object o) {

        return list.indexOf(o);

    }



    public int lastIndexOf(Object o) {

        return list.lastIndexOf(o);

    }



    public ListIterator<T> listIterator() {

        return list.listIterator();

    }



    public ListIterator<T> listIterator(int index) {

        return list.listIterator(index);

    }



    public List<T> subList(int fromIndex, int toIndex) {

        return list.subList(fromIndex, toIndex);

    }



    public synchronized void clear() { list.clear(); }

}