1

ListIterator が ConcurrentModificationException をスローするのを止める方法はありますか? これは私がやりたいことです:

  • 頻繁に実行される特定のメソッドを持つ一連のオブジェクトで LinkedList を作成します。
  • LinkedList 内のオブジェクトの前述のメソッドの実行を担当するスレッドの数 (N など) を設定します。たとえば、リストに k 個のオブジェクトがある場合、スレッド n はリストの n 番目のオブジェクトのメソッドを実行し、次に n+N 番目のオブジェクトに移動し、次に n+2N 番目に移動します。最初に戻るまで。

ここでの問題は、これらのオブジェクトの検索にあります。この作業を行うには、明らかに ListIterator を使用します。ただし、ドキュメントに従ってスローされる ConcurrentModificationException のおかげで、これはそれほど遠くないと予測しています。リストを変更可能にし、イテレータが気にしないようにしたい。実際、これらのオブジェクトは、リスト内の他のオブジェクトを作成および破棄することが予想されます。私はいくつかの回避策を考えました:

  • 新しい反復子を作成して破棄し、指定されたインデックスでオブジェクトを取得します。ただし、これは O(n) であり、望ましくありません。
  • 代わりに ArrayedList を使用してください。ただし、これも望ましくありません。削除は O(n) であり、リストを時々拡張する (そしておそらく縮小する?) 必要があるという問題があるためです。
  • 独自の LinkedList クラスを作成します。したくない。

したがって、私の質問です。ListIterator が ConcurrentModificationException をスローするのを止める方法はありますか?

4

4 に答える 4

2

あなたはパフォーマンスに関心があるようです。O(n)対O(1)アルゴリズムを使用した場合のパフォーマンスへの影響を実際に測定しましたか?何をしているのか、どれくらいの頻度で行っているのかによっては、CopyOnWriteArrayListスレッドセーフなを使用するだけでもよい場合があります。そのイテレータもスレッドセーフです。

主なパフォーマンスの低下は、変更操作(設定、追加、削除...)にあります。毎回新しいリストが再作成されます。

ただし、ほとんどのアプリケーションで十分なパフォーマンスが得られます。私は個人的にそれを使用してみて、アプリケーションのプロファイルを作成してパフォーマンスが十分に良好であることを確認し、そうである場合は次に進みます。そうでない場合は、他の方法を見つける必要があります。

于 2012-07-19T04:39:11.327 に答える
2

ListIteratorがConcurrentModificationExceptionをスローしないようにする方法はありますか?

この方法でこの質問をしているということは、スレッドを適切に使用してアプリケーションのパフォーマンスを向上させる方法についての理解が不足していることを示しています。

スレッドを使用する全体的な目的は、処理とIOを、互いに独立して並行して実行できる個別の実行可能なエンティティに分割することです。すべてのスレッドを同じように動作させるようにフォークしている場合、LinkedList各スレッドの同期の「ビュー」を維持するために必要な同期のオーバーヘッドがLinkedList並列実行。

問題は「停止する方法」ではなくConcurrentModificationException、「スレッドを使用してオブジェクトのリストの処理を改善するにはどうすればよいか」である必要があります。それは正しい質問です。

ExecutorServiceオブジェクトのコレクションを複数のスレッドと並行して処理するには、スレッドプールを使用する必要があります。次のコードのようなものでプールを作成します。LinkedList(この例では)の各エントリはJob、プール内のスレッドによって並行して処理されます。

// create a thread pool with 10 workers
ExecutorService threadPool = Executors.newFixedThreadPool(10);
// submit each of the objects in the list to the pool
for (Job job : jobLinkedList) {
    threadPool.submit(new MyJobProcessor(job));
}
// once we have submitted all jobs to the thread pool, it should be shutdown
threadPool.shutdown();
// wait for the thread-pool jobs to finish
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
synchronized (jobLinkedList) {
    // not sure this is necessary but we need to a memory barrier somewhere
}
...
// you wouldn't need this if Job implemented Runnable
public class MyJobProcessor implements Runnable {
    private Job job;
    public MyJobProcessor(Job job) {
        this.job = job;
    }
    public void run() {
        // process the job
    }
}
于 2012-07-19T13:58:26.500 に答える
0

@assyliasからの回答をご覧ください-彼のアドバイスは良いです。独自のリンクリストクラスを作成する場合は、スレッドセーフにする方法について慎重に検討する必要があることを付け加えておきます。

複数のスレッドがリストを同時に変更しようとした場合に、リストが破損する可能性があるすべての方法について考えてください。1つまたは2つのノードをロックするだけでは不十分です。例として、次のリストを見てください。

A-> B-> C-> D

別のスレッドがCを削除しているのと同じように、あるスレッドがBを削除しようとしていると想像してください。Bを削除するには、AからのリンクがBからCに「ジャンプ」する必要があります。同様に、Cを削除するには、Bからのリンクを変更してDにジャンプする必要がありますが、その時点でBがすでにリストから削除されている場合はどうなりますか?ノードがリストの近くの部分に同時に追加された場合にも、同様の問題が発生します。

ノードごとに1つのロックがあり、「削除」操作を実行するときに3つのノード(削除するノードとその前後のノード)をロックすると、スレッドセーフになると思います。また、ノードを追加するとき、およびリストをトラバースするときに、どのノードをロックする必要があるかについても慎重に検討する必要があります。デッドロックを回避するには、常に一定の順序でロックを取得する必要があります。リストをトラバースするときは、「ハンドオーバー」ロックを使用する必要があります(これにより、通常のJavaモニターを使用できなくなります。明示的に行う必要があります。オブジェクトをロックします)。

于 2012-07-19T04:31:54.833 に答える
0

1 つを使用Iteratorしてリストをスキャンし、 を使用しExecutorてスレッドのプールに渡すことにより、各オブジェクトで作業を行うことができます。簡単だ。このように作業単位をパッケージ化するにはオーバーヘッドがあります。メソッドを使用してリストを変更する場合は注意が必要ですがIterator、それによって問題が単純化される可能性があります。

それとも、あるパスで作業を実行してから、次のパスでリストの変更を行うことができますか?

N 個のリストに分割できますか?

于 2012-07-19T04:11:39.197 に答える