7

複数のスレッドからアクセスされているListオブジェクトがあります。ほとんどの場合、リストを更新するスレッドが1つあり、状況によっては2つのスレッドがあります。処理されているユーザー要求の数に応じて、このリストから読み取ることができるスレッドは1つから5つあります。このリストは、実行するタスクのキューではなく、同時に取得および更新されるドメインオブジェクトのリストです。

このリストへのアクセスをスレッドセーフにする方法はいくつかあります
。-同期ブロックを使用する
-通常のロックを使用する(つまり、読み取り操作と書き込み操作が同じロックを共有する)-ReadWriteLock
を 使用する-新しいConcurrentBLABLBAコレクションクラスの1つを使用する

私の質問:
重要なセクションには通常多くの操作(ほとんどの場合、リストから要素を追加/削除/挿入または取得するだけ)が含まれていないことを考えると、使用するのに最適なアプローチは何ですか?
上記にリストされていない別のアプローチをお勧めできますか?

いくつかの制約
-最適なパフォーマンスが重要であり、メモリ使用量はそれほど多くありません-ソートされたリストではありませんが(つまり、ComparableまたはComparatorを使用してソートされていないが、挿入順序に従って) 、 順序付けられたリスト(現在はArrayList
で同期されている)である必要がありますリストは大きく、最大100000のドメインオブジェクトが含まれるため、CopyOnWriteArrayListのようなものを使用することはできません -書き込み/更新の循環セクションは通常非常に高速で、単純な追加/削除/挿入または置換(設定) を実行します-読み取り操作は主に実行されますelementAt(index)はほとんどの場合呼び出しますが、一部の読み取り操作はバイナリ検索またはindexOf(element)を実行する場合があります。



-indexOf(..)のような操作はリストをトラバースしますが、リストに対する直接の反復は行われません。

4

5 に答える 5

3

順次リストを使用する必要がありますか? マップ タイプの構造がより適切な場合は、 を使用できますConcurrentHashMap。リストでReadWriteLockは、おそらく a が最も効果的な方法です。

OPの編集を反映するように編集:挿入順序のバイナリ検索? バイナリ検索で、タイムスタンプを保存して比較に使用しますか? ConcurrentSkipListMapその場合、タイムスタンプをキーとして、およびコンテナー (キーの順序を維持する)として使用できる場合があります。

于 2008-10-16T09:32:59.760 に答える
1

読書スレッドは何をしていますか?リストを反復処理している場合は、反復プロセス全体で誰もリストに触れないようにする必要があります。そうしないと、非常に奇妙な結果が得られる可能性があります。

必要なセマンティクスを正確に定義できれば、問題を解決できるはずですが、適切かつ効率的に行うには、独自のコレクションタイプを作成する必要があるかもしれません。あるいは、CopyOnWriteArrayListで十分な場合もありますが、コストがかかる可能性があります。基本的に、要件をより明確にできるほど、効率が向上します。

于 2008-10-16T08:58:14.463 に答える
1

同期を実装するラッパーを使用できます。

import java.util.Collections;
import java.util.ArrayList;

ArrayList list = new ArrayList();
List syncList = Collections.synchronizedList(list);

// make sure you only use syncList for your future calls... 

これは簡単な解決策です。より複雑なソリューションに頼る前に、これを試してみます。

于 2009-03-23T15:32:34.703 に答える
1

データベースに関するTelcontarの提案には賛成です。データベースは実際には、この規模のデータを管理し、スレッド間でネゴシエートするように設計されていますが、インメモリ コレクションはそうではありません。

データはサーバー上のデータベースにあり、クライアント上のローカル リストはユーザー インターフェイスのためのものだとおっしゃっています。一度に 100000 個のアイテムすべてをクライアントに保持したり、そのような複雑な編集を実行したりする必要はありません。クライアントに必要なのは、データベースへの軽量キャッシュであるように思えます。

クライアント上のデータの現在のサブセットのみを格納するキャッシュを一度に書き込みます。このクライアント キャッシュは、独自のデータに対して複雑なマルチスレッド編集を実行しません。代わりに、すべての編集をサーバーにフィードし、更新をリッスンします。サーバー上でデータが変更されると、クライアントは単純に古いデータを忘れて再度ロードします。指定された 1 つのスレッドのみが、コレクション自体の読み取りまたは書き込みを許可されます。このように、複雑な編集自体を必要とするのではなく、クライアントはサーバー上で行われている編集を単純にミラーリングします。

はい、これは非常に複雑なソリューションです。そのコンポーネントは次のとおりです。

  • 全体ではなく、データの範囲、たとえば項目 478712 から 478901 をロードするためのプロトコル
  • 変更されたデータに関する更新を受信するためのプロトコル
  • サーバー上の既知のインデックスによってアイテムを格納するキャッシュ クラス
  • サーバーと通信したキャッシュに属するスレッド。これは、コレクション自体に書き込む唯一のスレッドです
  • データの取得時にコールバックを処理するキャッシュに属するスレッド
  • ロードされたデータを受信できるようにするために UI コンポーネントが実装するインターフェース

最初の一刺しでは、このキャッシュの骨は次のように見えるかもしれません:

class ServerCacheViewThingy {
    private static final int ACCEPTABLE_SIZE = 500;
    private int viewStart, viewLength;
    final Map<Integer, Record> items
            = new HashMap<Integer, Record>(1000);
    final ConcurrentLinkedQueue<Callback> callbackQueue
            = new ConcurrentLinkedQueue<Callback>();

    public void getRecords (int start, int length, ViewReciever reciever) {
        // remember the current view, to prevent records within
        // this view from being accidentally pruned.
        viewStart = start;
        viewLenght = length;

        // if the selected area is not already loaded, send a request
        // to load that area
        if (!rangeLoaded(start, length))
            addLoadRequest(start, length);

        // add the reciever to the queue, so it will be processed
        // when the data has arrived
        if (reciever != null)
            callbackQueue.add(new Callback(start, length, reciever));
    }

    class Callback {
        int start;
        int length;
        ViewReciever reciever;
        ...
    }

    class EditorThread extends Thread {

        private void prune () {
            if (items.size() <= ACCEPTABLE_SIZE)
                return;
            for (Map.Entry<Integer, Record> entry : items.entrySet()) {
                int position = entry.key();
                // if the position is outside the current view,
                // remove that item from the cache
                ...
            }
        }

        private void markDirty (int from) { ... }

        ....
    }

    class CallbackThread extends Thread {
        public void notifyCallback (Callback callback);
        private void processCallback (Callback) {
            readRecords
        }
    }
}

interface ViewReciever {
    void recieveData (int viewStart, Record[] records);
    void recieveTimeout ();
}

明らかに、自分で記入しなければならない詳細がたくさんあります。

于 2008-10-16T15:09:04.590 に答える
1

これが問題の可能な解決策であるかどうかはわかりませんが...データベースマネージャーを使用してその膨大な量のデータを保持し、トランザクションを管理させることは理にかなっています

于 2008-10-16T10:05:14.233 に答える