ホームページ  >  記事  >  Java  >  Java並行プログラミング:並行コンテナCopyOnWriteArrayListの実装原理

Java並行プログラミング:並行コンテナCopyOnWriteArrayListの実装原理

php是最好的语言
php是最好的语言オリジナル
2018-07-30 11:41:051649ブラウズ

COWと呼ばれるCopy-On-Writeは、プログラミングで使用される最適化戦略です。基本的な考え方は、全員が最初から同じコンテンツを共有しており、誰かがコンテンツを変更したい場合は、そのコンテンツをコピーして新しいコンテンツを作成し、それを変更するという一種の遅延戦略です。 JDK1.5 以降、Java 同時実行パッケージは、CopyOnWrite メカニズムを使用して実装された 2 つの同時コンテナ (CopyOnWriteArrayList と CopyOnWriteArraySet) を提供します。 CopyOnWrite コンテナは非常に便利で、多くの同時シナリオで使用できます。

CopyOnWriteコンテナとは

CopyOnWriteコンテナとは、書き込み時にコピーされるコンテナです。一般に理解されているのは、要素をコンテナに追加するとき、要素を現在のコンテナに直接追加するのではなく、まず現在のコンテナをコピーして新しいコンテナを作成し、要素を追加した後、その新しいコンテナに要素を追加するということです。次に、元のコンテナの参照が新しいコンテナを指すようにします。この利点は、現在のコンテナーは要素を追加しないため、ロックせずに CopyOnWrite コンテナーで同時読み取りを実行できることです。したがって、CopyOnWriteコンテナも読み書き分離の考え方であり、読み書きは別のコンテナです。

CopyOnWriteArrayListの実装原理

CopyOnWriteArrayListを使用する前に、ソースコードを読んでどのように実装されているかを理解しましょう。次のコードは、CopyOnWriteArrayList の add メソッドの実装 (CopyOnWriteArrayList に要素を追加する) です。追加するときにロックする必要があることがわかります。そうしないと、複数のスレッドで書き込むときに N 個のコピーがコピーされます。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

/**

     * Appends the specified element to the end of this list.

     *

     * @param e element to be appended to this list

     * @return <tt>true</tt> (as specified by {@link Collection#add})

     */

    public boolean add(E e) {

    final ReentrantLock lock = this.lock;

    lock.lock();

    try {

        Object[] elements = getArray();

        int len = elements.length;

        Object[] newElements = Arrays.copyOf(elements, len + 1);

        newElements[len] = e;

        setArray(newElements);

        return true;

    finally {

        lock.unlock();

    }

    }

読み取り時に複数のスレッドが CopyOnWriteArrayList にデータを追加している場合、書き込み時に古い CopyOnWriteArrayList がロックされないため、読み取りでは引き続き古いデータが読み取られます。

1

2

3

public E get(int index) {

    return get(getArray(), index);

}

CopyOnWriteMap は JDK では提供されていません。CopyOnWriteArrayList を参照して実装できます。基本的なコードは次のとおりです。

12
3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

import java.util.Collection;

import java.util.Map;

import java.util.Set;

public class CopyOnWriteMap<K, V> implements Map<K, V>, Cloneable {

    private volatile Map<K, V> internalMap;

    public CopyOnWriteMap() {

        internalMap = new HashMap<K, V>();

    }

    public V put(K key, V value) {

        synchronized (this) {

            Map<K, V> newMap = new HashMap<K, V>(internalMap);

            V val = newMap.put(key, value);

            internalMap = newMap;

            return val;

        }

    }

    public V get(Object key) {

        return internalMap.get(key);

    }

    public void putAll(Map<? extends K, ? extends V> newData) {

        synchronized (this) {

            Map<K, V> newMap = new HashMap<K, V>(internalMap);

            newMap.putAll(newData);

            internalMap = newMap;

        }

    }

}

CopyOnWrite メカニズムを理解していれば、実装は非常に簡単です。さまざまな CopyOnWrite コンテナを実装して、さまざまなアプリケーション シナリオで使用できます。

CopyOnWriteのアプリケーションシナリオ

CopyOnWrite同時コンテナは、読み取りが多く書き込みが少ない同時シナリオで使用されます。たとえば、ホワイトリスト、ブラックリスト、製品カテゴリへのアクセスと更新のシナリオでは、ユーザーはこの Web サイトの検索ボックスにキーワードを入力してコンテンツを検索しますが、一部のキーワードは検索できません。検索できないキーワードはブラックリストに登録され、毎晩更新されます。ユーザーが検索を行うと、現在のキーワードがブラックリストに含まれているかどうかがチェックされ、含まれている場合は検索を実行できないというメッセージが表示されます。実装コードは次のとおりです:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

package com.ifeve.book;

import java.util.Map;

import com.ifeve.book.forkjoin.CopyOnWriteMap;

/**

 * 黑名单服务

 *

 * @author fangtengfei

 *

 */

public class BlackListServiceImpl {

    private static CopyOnWriteMap<String, Boolean> blackListMap = new CopyOnWriteMap<String, Boolean>(

            1000);

    public static boolean isBlackList(String id) {

        return blackListMap.get(id) == null false true;

    }

    public static void addBlackList(String id) {

        blackListMap.put(id, Boolean.TRUE);

    }

コード>    /**

     * 批量添加黑名单

     *

     * @param ids

     */

    public static void addBlackList(Map<String,Boolean> ids) {

        blackListMap.putAll(ids);

    }

}

コードは非常にシンプルですが、CopyOnWriteMapを使用する際に注意する必要があることが2つあります:

1. 展開のオーバーヘッドを削減します。実際のニーズに応じて CopyOnWriteMap のサイズを初期化し、書き込み中の CopyOnWriteMap 拡張のオーバーヘッドを回避します。

2. 一括追加を使用します。追加するたびにコンテナがコピーされるため、追加の数を減らすことでコンテナのコピー回数を減らすことができます。たとえば、上記のコードでは addBlackList メソッドを使用します。

CopyOnWriteのデメリット

CopyOnWriteコンテナには多くの利点がありますが、メモリ使用量とデータの一貫性という2つの問題もあります。そのため、開発時には注意が必要です。

メモリ使用量の問題。 CopyOnWrite のコピーオンライト メカニズムにより、書き込み操作が実行されると、古いオブジェクトと新しく書き込まれたオブジェクトという 2 つのオブジェクトが同時にメモリ内に常駐します (注: コピー中は、メモリ内の参照のみが保持されます)。コンテナは書き込み時にのみコピーされ、新しいオブジェクトが作成されて新しいコンテナに追加されますが、古いコンテナ内のオブジェクトはまだ使用されているため、オブジェクト メモリのコピーが 2 つ存在します。これらのオブジェクトが約 200M などの比較的大量のメモリを占有している場合、さらに 100M のデータを書き込むと 300M のメモリが占​​有されるため、Yong GC と Full GC が頻繁に発生する可能性があります。以前は、CopyOnWrite メカニズムを使用して大きなオブジェクトを毎晩更新するサービスをシステムで使用していました。その結果、毎晩 15 秒のフル GC が発生し、アプリケーションの応答時間も長くなりました。

メモリ使用量の問題に対処するには、コンテナ内の要素を圧縮することで、大きなオブジェクトのメモリ消費を減らすことができます。たとえば、要素がすべて 10 進数の場合、36 桁または 64 桁に圧縮することを検討できます。数字。または、CopyOnWrite コンテナを使用せず、ConcurrentHashMap などの他の同時コンテナを使用します。

データの整合性の問題。 CopyOnWrite コンテナはデータの最終的な整合性のみを保証できますが、データのリアルタイムの整合性は保証できません。したがって、書き込まれたデータをすぐに読み出したい場合は、CopyOnWrite コンテナを使用しないでください。

関連記事:

Java 同時プログラミング: CountDownLatch、CyclicBarrier、およびセマフォ

[JAVA 同時プログラミングの実践] ロックシーケンシャルデッドロック

関連ビデオ:

Java マルチスレッドと同時実行ライブラリの詳細アプリケーションビデオチュートリアル

以上がJava並行プログラミング:並行コンテナCopyOnWriteArrayListの実装原理の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。